标签:
最近在看一个PHP的扩展源码,编译的时候的遇到一个问题:
ld: 1 duplicate symbol for architecture x86_64
仔细看了一下源码,发现在头文件中 出现了全局变量的定义
ZEND_DECLARE_MODULE_GLOBALS(xx)
简单开来,可以这么理解
// t1.h #ifndef T1_H #define T1_H int a = 0; #endif //------------------ //t1.c #include "t1.h" #include "t2.h" int main(){ return 0; } //----------------- //t2.h #include "t1.h" //empty //---------------- //t2.c #include "t2.h" //empty //-------
那么第一个问题,#ifndef 的这个宏 是否应该防止了 多重定义?
答案:是。但是是在单个编译单元中(wiki translation unit)。
我们知道,一个完整的编译的过程是经过
one.c --> PREPROCESSOR -> tmp.c(temporary) -> COMPILER -> one.obj -> LINKER -> one.exe
这三个过程的,而在预编译阶段,便会把include的文件展开,我们使用cc -E 命令来查看t1.c的预编译的结果:
? t cc -E t1.c # 1 "t1.c" # 1 "<built-in>" 1 # 1 "<built-in>" 3 # 321 "<built-in>" 3 # 1 "<command line>" 1 # 1 "<built-in>" 2 # 1 "t1.c" 2 # 1 "./t1.h" 1 int a = 0; # 3 "t1.c" 2 # 1 "./t2.h" 1 # 4 "t1.c" 2 int main(void){ return 0; }
看到编译器把 t1.h 做了展开,我们看到了 a的定义。
而在t2.c 的预编译结果里,我们同样看到了a的展开定义:
? t cc -E t2.c # 1 "t2.c" # 1 "<built-in>" 1 # 1 "<built-in>" 3 # 321 "<built-in>" 3 # 1 "<command line>" 1 # 1 "<built-in>" 2 # 1 "t2.c" 2 # 1 "./t2.h" 1 # 1 "./t1.h" 1 int a = 0; # 2 "./t2.h" 2 # 2 "t2.c" 2
那么,为神马 #ifdef ( include guards )没有起到 防止重定义的作用呢?
原因在于 include guards 只在同一个编译单元(一个c文件和include的文件的编译过程)内起作用,两个编译单元是编译过程是分开的,所以无法察觉到另外一个里面的#ifdefine内容
t1.c -> t1.s -> t2.o *-> - t.otu / t2.c -> t2.s -> t2.o
所以,在头文件中是不应该define 变量,只应该declare。
那么如果我偏要呢?
如果是函数,有人给出这么个办法,添加inline或者static 关键字。或者有人直接这么搞:
#ifdef DEFINE_GLOBALS #define EXTERN #else #define EXTERN extern #endif EXTERN int global1; EXTERN int global2;
那么,回到最初的问题,头文件中确实是不可以定义变量的,而且我也没有看到类似特殊的宏定义处理办法,那么这个问题他是如何处理的呢?难道在编译阶段处理?
且等下节。。。
标签:
原文地址:http://www.cnblogs.com/Sorean/p/4705783.html