在编译时,编译器将每个全局符号作为强符号或弱符号导出到汇编程序,汇编程序在可重定位对象文件的符号表中隐式地编码这些信息。函数和初始化的全局变量得到强符号。未初始化的全局变量得到弱符号。 对于以下示例程序,buf、bufp0、main和swap是强符号;bufp1是一个弱符号。
/* main.c */ void swap(); int buf[2] = {1, 2}; int main() { swap(); return 0; } /* swap.c */ extern int buf[]; int *bufp0 = &buf[0]; int *bufp1; void swap() { int temp; bufp1 = &buf[1]; temp = *bufp0; *bufp0 = *bufp1; *bufp1 = temp; } |
考虑到强符号和弱符号的概念,Unix链接器使用以下规则来处理多个定义的符号: 规则1: 不允许使用多个强符号(具有相同的变量名)。 规则2: 给定一个强符号和多个弱符号,选择强符号。 规则3: 给定多个弱符号,请选择任意一个弱符号。 例如,假设我们试图编译并链接以下两个C模块:
/* foo1.c */ int main() { return 0; } /* bar1.c */ int main() { return 0; } |
在这种情况下,链接器将生成一条错误消息,因为强符号main被定义了多次( 规则1 ):
$ gcc foo1.c bar1.c /tmp/cca015022.o: In function ‘main’: /tmp/cca015022.o(.text+0x0): multiple definition of ‘main’ /tmp/cca015021.o(.text+0x0): first defined here
类似地,链接器将为以下模块生成错误消息,因为强符号x定义了两次( 规则1 ):
/* foo2.c */ int x = 15213; int main() { return 0; } /* bar2.c */ int x = 15213; void f() { } |
然而,如果x在一个模块中未初始化,那么链接器将悄悄地选择在另一个模块中定义的强符号( 规则2 )与以下程序中的情况一样:
/* foo3.c */ #include <stdio.h> void f( void ); int x = 15213; int main() { f(); printf ( "x = %d" , x); return 0; } /* bar3.c */ int x; void f() { x = 15212; } |
在运行时,函数f()将x的值从15213更改为15212,这可能会让函数main的作者感到意外!请注意,链接器通常不表示它检测到了x的多个定义。
$ gcc -o gfg foo3.c bar3.c $ ./gfg x = 15212
如果x有两个弱定义,同样的事情也会发生( 规则3 ):
/*a.c*/ #include <stdio.h> void b( void ); int x; int main() { x = 2016; b(); printf ( "x = %d " ,x); return 0; } /*b.c*/ #include <stdio.h> int x; void b() { x = 2017; } |
这个 规则2和3的适用 可能会引入一些不小心的程序员无法理解的潜在运行时错误,尤其是在重复符号定义具有不同类型的情况下。 例如:“x”在一个模块中定义为int,在另一个模块中定义为double。
/*a.c*/ #include <stdio.h> void b( void ); int x = 2016; int y = 2017; int main() { b(); printf ( "x = 0x%x y = 0x%x " , x, y); return 0; } /*b.c*/ double x; void b() { x = -0.0; } |
执行:
$ gcc a.c b.c -o geeksforgeeks $ ./geeksforgeeks x = 0x0 y = 0x80000000
这是一个微妙而令人讨厌的错误,尤其是因为它在没有来自编译系统的警告的情况下悄无声息地发生,并且因为它通常在程序执行的很晚的时候出现,远离错误发生的地方。在一个包含数百个模块的大型系统中,此类错误极难修复,尤其是因为许多程序员不知道链接器是如何工作的。如果有疑问,可以使用gcc-fno公共标志等标志调用链接器,如果遇到多个定义的全局符号,就会触发错误。
资料来源:http://csapp.cs.cmu.edu/public/ch7-preview.pdf
本文由 萨希尔·拉吉普特 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以使用 贡献极客。组织 或者把你的文章寄到contribute@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。
如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。