弱符号与强符号
对于C/C++语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。我们也可以通过GCC的__attribute__((weak))将一个强符号变为弱符号。强符号和弱符号都是针对定义来说的,不是针对符号的引用。
extern int ext;
int weak; //弱符号
int strong = 1; //强符号
__attribute__((weak)) weak2 = 2;//弱符号
int main()
{
return 0;
}
针对强弱符号的概念,链接器就会按如下规则处理与选择多次定义的全局符号:
- 规则1:不允许强符号被多次定义(即不同的目标文件不能有同名的强符号);如果有多个强符号的定义,则编译器报符号重复定义错误。
- 规则2:如果一个符号在某个目标文件中是强符号,在其它文件中是弱符号,那么选择强符号。
- 规则3:如果一个符号在所有目标文件中都是弱符号,那么选择其中占用空间最大的一个。
弱引用与强引用目前我们看到的对外部目标文件的符号引用在目标文件被最终链接成可执行文件时,它们必须被正确决议,如果没有找到该符号的定义,链接器就会报符号为定义错误,这种被称为强引用。与之相对应的还有弱引用,在处理弱引用时,如果该符号有定义,则链接器将该符号的引用决议;如果该符号未被定义,则链接器对于该引用不报错。
我们用下面的代码来验证一下:
1.c
#include<stdio.h>
extern int a;
extern void fun();
extern void func();
int main()
{
fun();
func();
printf("1.c a = %d\n", a);
}
2.c
#include<stdio.h>
int a = 5;
void fun()
{
printf("2.c a = %d\n", a);
}
3.c
#include<stdio.h>
int a;
void func()
{
printf("3.c a = %d\n", a);
}
makefile文件
main: 1.o 2.o 3.o
gcc 1.o 2.o 3.o -o main
1.o: 1.c
gcc -c 1.c
2.o: 2.c
gcc -c 2.c
3.o: 3.c
gcc -c 3.c
clean:
rm 1.o 2.o 3.o
上面是三个文件中的代码,在1.c文件中定义了main函数,读者可以自己判断一下1.c文件中打印的结果。
我们来看一下打印的结果
结果:发现打印的结果都为5,那是怎么回事呢?
解析
- 在2.c文件中,我们定义了a变量并为其赋值5,此时在2.c文件中的a为强符号,在3.c文件虽然我们也定义了a变量,但是没有显示的为其赋初值(在3.c中它的值是0)其为弱符号。当我们将其链接为一个可执行文件时,编译器在全局表中发现有两个a变量,它就会进行决策,将强符号(规则2)的a变量当作这一个可执行文件的a变量,所以a的值5,当func函数执行时,它需要在可执行文件中查找a的值,此时也就变为了5。
读者如果想要深入了解可以看之前我写的一篇博客解析ELF文件。