2013-04-18 50 views
2

這個問題是關於gcc的構造函數,編譯&鏈接是對的,但它不能運行。GCC構造函數不執行

有交流轉換器:

UTEST_BEGIN() 
UID(a_test) 
{ 
    printf("a test"); 
    return true; 
} 
UTEST_END(a) 

b.c是simlar:

UTEST_BEGIN() 
UID(b_test) 
{ 
    printf("b test"); 
    return true; 
} 
UTEST_END(b) 

代碼對象是使用UID()鏈接一些測試功能。我的第一個版本添加了UTEST_BEGIN()UTEST_END()來包含UID(),最後我意識到UTEST_BGIN()UTEST_END()不是必需的,當我改變它們時會得到不可預知的結果。

當我更改UTEST_BEGIN(),UID(),UTEST_END()的定義時,我得到了不同的結果。

基本想法來自can-i-auto-collect-a-list-of-function-by-c-macro

測試1:

#define UTEST_BEGIN()         \ 
static const bool __m_en = true;      \ 
static struct __uti *__m_uti_head = NULL; 



bool utest_item_list_add_global(struct __uti *uti); 
#define UID(f)               \ 
static bool __uti_##f(void);           \ 
__attribute__((constructor))           \ 
static void uti_construct_##f(void)          \ 
{                  \ 
    printf("%s\n", #f); \ 
    static struct __uti __m_uti_##f = {NULL, this_file_id, __uti_##f, #f };  \ 
    utest_item_list_add_global(&__m_uti_##f);       \ 
}                  \ 
static bool __uti_##f(void) 


bool unit_test_item_pump_do(int file_id, bool (*f)(void), const char *f_name); 
#define UTEST_END(file_name)           \ 
bool unit_test_##file_name(void)          \ 
{                  \ 
    if (!__m_en)              \ 
      return true;            \ 
    struct __uti *cur;             \ 
    for(cur = __m_uti_head; cur; cur = cur->next) {      \ 
      unit_test_set_run_last_line(__LINE__);      \ 
      if (!unit_test_item_pump_do(this_file_id, cur->f, cur->f_name)) \ 
        return false;          \ 
    }                 \ 
    return true;              \ 
} 

我得到正確的結果。我可以通過鏈接調用__uti_a_test()和__uti_b_test()。實際上,__uti_xxx()鏈接與__m_uti_head不相關,所以我想刪除UTEST_BEGIN()& UTEST_END()。

運行gcc -E交流,宏如延伸:

static const bool __m_en = 1; 
static struct __uti *__m_uti_head = ((void *)0); 

static bool __uti_a_test(void); 
__attribute__((constructor)) 
static void uti_construct_a_test(void) 
{ 
    static struct __uti __m_uti_a_test = {((void *)0), file_id_a, __uti_a_test, "a_test" }; 
    utest_item_list_add_global(&__m_uti_a_test); 
} 
static bool __uti_a_test(void) 
{ 
    printf("a test"); 
    return 1; 
} 


bool unit_test_a(void) 
{ 
    if (!__m_en) 
     return 1; 
    struct __uti *cur; 
    for(cur = __m_uti_head; cur; cur = cur->next) { 
     unit_test_set_run_last_line(19); 
     if (!unit_test_item_pump_do(file_id_a, cur->f, cur->f_name)) 
      return 0; 
    } 
    return 1; 
} 

試驗2:

#define UTEST_BEGIN() 



bool utest_item_list_add_global(struct __uti *uti); 
#define UID(f)               \ 
static bool __uti_##f(void);           \ 
__attribute__((constructor))           \ 
static void uti_construct_##f(void)          \ 
{                  \ 
    printf("%s\n", #f);             \ 
    static struct __uti __m_uti_##f = {NULL, this_file_id, __uti_##f, #f };  \ 
    utest_item_list_add_global(&__m_uti_##f);       \ 
}                  \ 
static bool __uti_##f(void) 


#define UTEST_END(file_name) 

UID()的定義同試驗1我保持UTEST_BEGIN()& UTEST_END()爲空。編譯&鏈接是正確的,但uti_construct_a_test()& uti_construct_b_test()不執行。

運行gcc -E交流,宏如延伸:

static bool __uti_a_test(void); 
__attribute__((constructor)) 
static void uti_construct_a_test(void) 
{ 
    static struct __uti __m_uti_a_test = {((void *)0), file_id_a, __uti_a_test, "a_test" }; 
    utest_item_list_add_global(&__m_uti_a_test); 
} 
static bool __uti_a_test(void) 
{ 
    printf("a test"); 
    return 1; 
} 

的utest_item_list_add_global()是在其他.c文件存在,則功能添加一個節點到鏈接:

static struct __uti *m_uti_head = NULL; 
bool utest_item_list_add_global(struct __uti *uti) 
{ 
     if (NULL == m_uti_head) { 
       m_uti_head = uti; 
       return true; 
     } 

     struct __uti *tail = m_uti_head; 
     while (NULL != tail->next) 
       tail = tail->next; 
     tail->next = uti; 
     return true; 
} 

擴大的macor似乎是正確的。我認爲問題出現在鏈接階段,對嗎?

+0

您的代碼無法編譯。它也看起來像一個越野車,不可維護,不可讀的代碼。不要負面;)。不過,你可能會通過使用例如'cpp a.c> a.i'來擴展宏來了解發生了什麼。看看'a.i'的底部。該文件將在編譯過程中進一步用於創建彙編代碼。 – Runium

+0

使用GCC __attribute __((構造函數))功能,所以任何由UID()定義的函數都可以在鏈接中鏈接。而讓用戶只需思考和編寫測試功能體,就不用考慮測試功能。只有一條線。我覺得這很酷。 – husthl

+2

「真的很酷」*確實會導致長期難以維護的代碼。發明自己的語法(看起來就像是你在做什麼)更容易這樣做。我認爲@Sukminder有一個很好的觀點。 –

回答

3

我發現GCC 屬性((構造函數))具有如下的事實:

cons.c是包含構造函數的文件。

  1. 如果cons.c文件中只存在構造函數,將其編譯爲靜態庫,然後將其與main()鏈接,構造函數將被忽略。
  2. 如果在cons.c中存在main.c中調用的任何函數,則將cons.c編譯爲靜態庫,然後將其與main()鏈接,構造函數將在main之前調用。
  3. 如果使用「gcc main.c cons.c」,構造函數將在main之前調用。

缺點:C:

#include <stdio.h> 
static void __attribute__((constructor)) construct_fun(void) 
{ 
     printf("this is a constructor\n"); 
} 

void cons(void) 
{ 
     printf("this is cons\n"); 
} 

試驗1:

main.c中:

#include <stdio.h> 
int main(void) 
{ 
     printf("this is main\n"); 
} 

編譯由:

gcc -c cons.c 
ar cqs libcon.a cons.o 
gcc main.c libcon.a 

輸出是: 這是主要

試驗2:

main.c中:

#include <stdio.h> 
extern void cons(void); 
int main(void) 
{ 
     cons(); 
     printf("this is main\n"); 
} 

編譯由:

gcc -c cons.c 
ar cqs libcon.a cons.o 
gcc main.c libcon.a 

輸出:

this is a constructor 
this is cons 
this is main 

測試3:

的main.c

#include <stdio.h> 
int main(void) 
{ 
     printf("this is main\n"); 
} 

編譯通過:

gcc main.c cons.c 

輸出:

this is a constructor 
this is main 

運行 「GCC -v」,輸出:

使用內置規格。 COLLECT_GCC = gcc COLLECT_LTO_WRAPPER =/usr/libexec/gcc/i686-redhat-linux/4.7.2/lto-wrapper 目標:i686-redhat-linux 配置:../configure --prefix =/usr - -mandir =/usr/share/man --infodir =/usr/share/info --with-bugurl = http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads = posix --enable-checking = release --disable-build-with-cxx --disable-build-poststage1-with-cxx --with-system-zlib --enable -__ cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object - enable-linker-build-id --with-linker-hash-style = gnu --enable-languages = c,C++,objc,obj-C++,java,fortran,ada,go,lto --enable-plugin - enable-initfini-array --enable-java-awt = gtk --disable -dssi --with-java -home =/usr/lib/jvm/java-1.5.0 -gcj-1.5.0.0/jre --enable -libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar =/usr/share/java/eclipse-ecj.jar --disable -libjava-mu ltilib --with-ppl --with-cloog --with-tune = generic --with-arch = i686 --build = i686-redhat-linux 線程模型:posix gcc版本4.7.2 20121109(Red Hat 4.7 .2-8)(GCC)

我的問題是:

只有構造函數存在於一個.c文件編譯爲靜態庫,爲什麼GCC忽略結構?如何避免它?

+0

當構造函數在靜態庫中的對象文件中時,鏈接器將不會拉入該構造函數除非物體由於某種原因被拉入。在你的測試2中,因爲main()調用cons(),所以目標文件(和其中的構造函數)被鏈接進來。在測試1中沒有任何東西導致鏈接器將對象從圖書館。在測試3中,對象被顯式鏈接,所以鏈接器包含它(只有當某些東西'觸發'時才從庫中拉取項目 - 否則在每個可執行文件中總是會獲取庫中的所有內容)。 –

+0

是的,[c-linker-issues](http://stackoverflow.com/questions/1624403/c-linker-issues)描述了同樣的問題。 GCC __attribute __((構造函數))後來鏈接,可能是鏈接應該考慮這個問題,因爲構造函數不同於其他函數,構造函數沒有任何直接調用者。或者我不應該使用C語言的這種擴展。 – husthl

相關問題