程序编译的链接阶段中,将函数和变量统称为符号,函数名或者变量名称为符号名。
已初始化的(初始化值不为0)全局变量在连接中为强符号,未初始化或者初始化为0的全局变量为弱符号。在多个源文件中,可以允许声明多个同名的全局变量,但只能有一个初始化,即只允许(同时)存在一个强符号。
例如;Test1.c中:
double g_num = 10;
int test()
{
printf("test1.c:sizeof(g_num)=%d \n value of g_num = %f\n",sizeof(g_num),g_num);
}
Main.c中
int g_num;
int main(void)
{
printf("main.c:sizeof(g_num) =%d \n value of g_num = %d\n",sizeof(g_num),g_num);
test();
}
编译: my@ubuntu:~/$gcc haier_main.c test1.c -o testout
/usr/bin/ld: Warning: size of symbol`g_num' changed from 4 in /tmp/ccFWHHgz.o to 8 in /tmp/ccqvBCSU.o
my@ubuntu:~/ $ ./testout
main.c: sizeof(g_num) =4
value of g_num = 0
test1.c:sizeof(g_num)= 8
value of g_num = 10.000000
注意上面的警告;是因为在连接时,保存在符号表中的g_num是double类型的,main,c中声明的g_num可以看做是对test1.c中的g_num 赋值为0.,查看符号表如下:
my@ubuntu:~/ $ readelf -s testout
…….
__libc_start_main@@GLIBC_
64: 08048660 97 FUNC GLOBAL DEFAULT 13 __libc_csu_init
65: 0804a028 0 NOTYPE GLOBAL DEFAULT ABS _end
66: 08048360 0 FUNC GLOBAL DEFAULT 13 _start
67: 08048728 4 OBJECT GLOBAL DEFAULT 15 _fp_hw
68: 0804a020 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
69: 08048502 296 FUNC GLOBAL DEFAULT 13 main
70: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
71:0804a018 8 OBJECT GLOBAL DEFAULT 24 g_num
72: 080482d4 0 FUNC GLOBAL DEFAULT 11 _init
73:0804862c 39 FUNC GLOBAL DEFAULT 13 test
另外,如果将test1.c中修改为:
int g_num = 10;
int test()
{
printf("test1.c:sizeof(g_num)=%d \n value of g_num = %d\n",sizeof(g_num),g_num);
}
Main,c 中修改为:double g_num;
编译后,则打印结果为:
my@ubuntu:~/ $ gcc haier_main.c test1.c -o testout
gcc: error: haier_main.c: No such fileor directory
my@ubuntu:~/ $ gcc haier_main.c test1.c -o testout
/usr/bin/ld: Warning: alignment 4 ofsymbol `g_num' in /tmp/cc6qqr8P.o is smaller than 8 in /tmp/ccWo2XAC.o
/usr/bin/ld: Warning: size of symbol`g_num' changed from 8 in /tmp/ccWo2XAC.o to 4 in /tmp/cc6qqr8P.o
my@ubuntu:~/ $ ./testout
main.c: sizeof(g_num) =8
value of g_num = 0.000000
test1.c:sizeof(g_num)= 4
value of g_num = 10
my@ubuntu:~/ $
使用readelf –stestout查看结果如下:
69: 08048502 296 FUNC GLOBAL DEFAULT 13 main
70: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
71: 0804a018 4 OBJECT GLOBAL DEFAULT 24 g_num
72: 080482d4 0 FUNC GLOBAL DEFAULT 11 _init
73:0804862c 39 FUNC GLOBAL DEFAULT 13 test
以上可以看出,连接器无法判断各个符号的类型是否一致。当存在多个同名的符号时,连接后输出文件中该变量所占的空间的大小以输入文件(符号)中最大的那个为准,这也是连接中的common类型的连接规则。但是当多个符号中有一个是强符号时,那么最终输出结果中的符号所占的空间与强符号相同。
再者如果在,main.c中定义了未初始化的全局变量,在可执行文件中可以发现已经为这些变量分配了虚拟空间的地址:
double g_num;
int g_flag;
int g_flag1;
int main(void)…
my@ubuntu:~/ $ readelf -s testout
Symbol table'.dynsym' contains 6 entries:
Num: Value Size Type Bind Vis Ndx Name
….
54: 0804a01c 0 NOTYPE GLOBAL DEFAULT ABS _edata
55: 0804a024 4 OBJECT GLOBAL DEFAULT 25 g_flag
……
72: 0804a018 4 OBJECT GLOBAL DEFAULT 24 g_num
73: 0804a028 4 OBJECT GLOBAL DEFAULT 25 g_flag1
74: 080482d4 0 FUNC GLOBAL DEFAULT 11 _init
75: 0804862c 39 FUNC GLOBAL DEFAULT 13 test
注意其中第二列表示的在可执行文件加载时的虚拟空间的地址(即程序在进程所使用的虚拟地址),在Linux中elf可执行文件默认从地址0x08048000开始分配。
未初始化的全局变量或静态变量在编译的时候不会分配内存,可能在加载的时候会为之分配。但是,对于已经初始化的全局或静态变量在编译的时候就需要为之分配内存保存那些初始化的值,这时它们一般会被编译进data段(在linux下),这时这些变量所占用的空间也就被分配了,在加载的时候,data段会被整个加载入内存,因此其对应的空间也被加载到了内存,而不是在堆中再进行分配,堆中都是用malloc、new之类的动态申请的,而不是编译时候就占用的。