时间:2021-05-20
之前在extern “C” 用法详解中已经提到过符号的概念,它是编译器对变量和函数的一种标记,编译器对C和C++代码在生产符号时规则也是不一样的,符号除了本身名字的区别外,还有强符号和弱符号之分
我们先看一段简单的代码
复制代码 代码如下:
void hello();
int main()
{
hello();
return 0;
}
很显然,这段代码是没法链接通过的,它会报错undefined reference to hello,说的是hello未定义,因为这里我们只声明了函数hello,而没有定义它。但是我们把代码稍作修改如下
复制代码 代码如下:
__attribute__((weak)) void hello();
int main()
{
hello();
return 0;
}
这时你会发现,编译链接都可通过,但是运行会报错,因为这时我们将hello声明为了弱符号,在链接时弱符号会被链接器当做0,执行一个地址为0的函数当然会报错,改为如下代码就不会报错了,只是它没有任何输出
复制代码 代码如下:
__attribute__((weak)) void hello();
int main()
{
if(hello)
hello();
return 0;
}
编译器认为,函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号,链接器在处理强符号和弱符号时有如下规则
1.不同目标文件中,不允许有同名的强符号
2.如果一个符号在某个目标文件中是强符号,在其它目标文件中为弱符号,选择强符号
3.如果一个符号在所有目标文件中都是弱符号,选择占用空间最大的,比如目标文件A中有double global_var,文件B中有int global_var,double占用8字节,大于int的4字节,A和B链接后,符号global占8字节
对此我们可以简单的验证一下,有如下两个文件
复制代码 代码如下:
char global_var;
int main()
{
return 0;
}
int global_var;
全局变量global_var在两个文件中都没有初始化,因此都是弱符号,执行编译命令gcc 1.c 2.c,用readelf查看符号表readelf -s a.out,为了查看方便我们只输出最后几行
复制代码 代码如下:
Num: Value Size Type Bind Vis Ndx Name
62: 0000000000600818 4 OBJECT GLOBAL DEFAULT 25 global_var
63: 0000000000400474 11 FUNC GLOBAL DEFAULT 13 main
64: 0000000000400358 0 FUNC GLOBAL DEFAULT 11 _init
这里符号global_var占用的size是4,说明链接器选择的是占用空间更大的int global_var,我们再稍作修改,将1.c中的全局变量初始化,如下
复制代码 代码如下:
char global_var = 1;
int main()
{
return 0;
}
int global_var;
这时1.c中的global_var为强符号,2.c中的global_var为弱符号,同样编译之后用readelf查看符号表readelf -s a.out如下
复制代码 代码如下:
Num: Value Size Type Bind Vis Ndx Name
62: 0000000000600818 1 OBJECT GLOBAL DEFAULT 25 global_var
63: 0000000000400474 11 FUNC GLOBAL DEFAULT 13 main
64: 0000000000400358 0 FUNC GLOBAL DEFAULT 11 _init
此时符号global_var占用的size是1,说明链接器选择的是强符号
在写代码时应该尽量避免有不同类型的符号,否则会引发非常诡异且不易察觉的错误,为了避免可以采取如下措施:
1.上策:消除所有的全局变量
2.中策:将全局变量声明为static类型,并提供接口供访问
3.下策:全局变量一定要初始化,哪怕初始化为0
4.必备:打开gcc的-fno-common选项,它会禁止有不同类型的符号
说了这么多,好像在说应该尽量用强符号,那弱符号有什么用呢,所谓存在即合理,有时候我们甚至需要显示定义弱符号,这对库函数会非常有用,比如库中的弱符号可以被用户自定义的强符号覆盖,从而实现自定义的库版本,或者在使用某些扩展功能时,用户可以定义一个弱符号,当链接了该功能时,功能模块可以正常使用,如果去掉功能模块,程序也可正常链接,只是缺少某些功能而已,比如我们可以通过下面的代码判断程序是否链接了pthread库,从而决定执行什么样的操作
复制代码 代码如下:
#include <stdio.h>
#include <pthread.h>
__attribute__((weak)) int pthread_create(
pthread_t*,
const pthread_attr_t*,
void*(*)(void*),
void*);
int main()
{
if (pthread_create)
{
printf("This is multi-thread version!\n");
}
else
{
printf("This is single-thread version!\n");
}
return 0;
}
编译运行结果如下
复制代码 代码如下:
$ gcc test.c
$ ./a.out
This is single-thread version!
$ gcc test.c -lpthread
$ a.out
This is multi-thread version!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
首先我表示很悲剧,在看《程序员的自我修养--链接、装载与库》之前我竟不知道C有强符号、弱符号、强引用和弱引用。在看到3.5.5节弱符号和强符号时,我感觉有些困惑
在c语言中数字字符是字符类型,是按照字符串的处理方式处理字符,是占位符为%c。 字符指类字形单位或符号,包括字母、数字、运算符号、标点符号和其他符号,以及一些
C语言中有符号数和无符号数进行运算(包括逻辑运算和算术运算)默认会将有符号数看成无符号数进行运算,其中算术运算默认返回无符号数,逻辑运算当然是返回0或1了。un
Javascript有两种方法来判断两种值是否相等。相等符号相等符号由两个等号组成:==Javascript是弱类型语言。这就意味着相等符号为了比较两个值将会强
C语言中无符号与有符号问题unsignedchara[5]={12,36,96,128,182};a[]范围为0~256.数组中数都有效。chara[5]={1