对于链接器来说,所有的全局符号可分为两种:强符号(Strong symbols),弱符号(Weak symbols)。gcc的attribute中有个attribute((weak)),就是用来声明这个符号是弱符号的。gcc手册中这样写道:
The weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions which can be overridden in user code, though it can also be used with non-function declarations. Weak symbols are supported for ELF targets, and also for a.out targets when using the GNU assembler and linker.
对于这个gcc扩展,这里作了一个简洁的介绍。我们来看更通用的情况。;)
一般来说,函数和已初始化的变量是强符号,而未初始化的变量是弱符号。对于它们,下列三条规则适用:
1. 同名的强符号只能有一个。
2. 有一个强符号和多个同名的弱符号是可以的,但定义会选择强符号的。
3. 有多个弱符号时,链接器可以选择其中任意一个。
这三条规则看起来很好理解,其实不然,尤其是当这些弱符号类型和强符号不同时!表面上看起来正确的进程会导致严重的错误!考虑下面这个csapp中的例子:
===a.c===
int x=7;
int y=5;
p1()
===b.c===
double x;
p2()
我们把它们一起编译,并且在p2()函数中给x赋值,你会发现,y也改变了! 虽然x被看作是double,但其定义会取a.c中的int x,也就是说,在b.c中会把a.c中的int x当double来用!这当然是错误!之所以会这样,就是因为上面的规则2。避免这种错误的一个方法是,给gcc加上-fno-common选项。
关于弱符号,man手册中这样解释到: