2013-04-18 89 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