3

这个问题是关于 gcc 构造函数的,编译和链接是正确的,但它没有运行。

有交流:

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

bc 是类似的:

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 ac,宏扩展为:

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 ac,宏扩展为:

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;
}

扩展的宏似乎是正确的。我认为问题出在链接阶段,对吗?

4

1 回答 1

4

我发现 gcc属性((构造函数))具有以下事实:

cons.c 是一个包含构造函数的文件。

  1. 如果 cons.c 文件中只有构造函数,将其编译为静态库,然后与 main() 链接,构造函数将被忽略。
  2. 如果 main.c 中调用的函数在 cons.c 中存在,则将 cons.c 编译为静态库,然后将其与 main() 链接,构造函数将在 main 之前调用。
  3. 如果使用“gcc main.c cons.c”,构造函数将在main之前调用。

缺点:

#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:

主.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:

主.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:

主程序

#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-multilib --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 忽略该构造?如何避免?

于 2013-04-23T05:56:41.277 回答