本文转载自微信公众号《编程珠玑》,作者守望老师。转载本文请联系编程诸暨(ID:shouwangxiansheng)公众号。可能有些小伙伴的第一反应是一定不能编译://来源:公众号【编程珠玑】//作者:看守先生//fun.c#includevoidfun(){printf("编程明珠\n");}//main.c#includevoidfunc(){printf("公众号\n");}intmain(void){func();return0;}编译:$gcc-omainmain.cfun.c/tmp/ccKeACRk.o:Infunction`fun':fun.c:(.text+0x0):multipledefinitionof`fun'/tmp/cc4ezgqh.o:main.c:(。text+0x0):firstdefinedherecollect2:error:ldreturned1exitstatus可以看到这里报错了,因为重复定义了fun。但是重复定义会报错,会不会编译不通过?不是全部!再看下面这段代码://来源:公众号【编程珠玑】//作者:守望先生//var.cintnum;voidchange(){num=1023;}//main.c#includevoidchange();intnum=1024;intmain(void){printf("before:numis%d\n",num);change();printf("after:numis%d\n",num);return0;}输出结果:before:numis1024after:numis1023从结果可以看出,虽然num定义了两次,但是仍然可以正常编译运行。为什么是这样?Symbols在讲解今天要分享的内容之前,我们先来简单了解一下什么是Symbols。《hello程序是如何变成可执行文件的》中提到,ELF文件生成的最后阶段是链接,链接阶段是根据符号完成的。每个目标文件都有一个符号表。链接过程是通过符号表中的符号将不同的目标文件“粘合”在一起,形成最终的库或可执行文件。查看目标文件的符号信息也很简单://symbol.c#includeintsymbol=1024;intfunc_symbol(){return0;}编译:$gccsmbol.c#Compile$nmsymbol.o#查看符号信息0000000000000000Tfunc_symbol0000000000000000Dsymbol可以通过nm命令查看符号信息,这里是我们的func_symbol函数和全局变量symbol的符号。关于nm的使用,在《几个命令了解ELF文件的秘密》中也有介绍。除了上面提到的全局符号外,目标文件中还有其他符号信息,但这不是本文的重点。强符号和弱符号对于C/C++语言来说,编译器默认的函数和已初始化的全局变量都是强符号,未初始化的全局变量是弱符号。当然你也可以通过__attribute__((weak))将强符号定义为弱符号。使用下面的例子,看看哪些是强符号,哪些是弱符号:#includeintweak;//未初始化的全局变量,弱符号intstrong=1024;//已初始化的全局变量,强符号__attribute__((weak))intweak1=2222;//标识修饰的弱符号intmain(void){printf("编程明珠\n");return0;}注意这里的强符号和弱符号是为了定义。当名称相同时,使用哪一个?对于多个定义,即当题目中提到的变量同名时,链接器有它的处理规则:1.强符号不允许重复2.如果有一个强符号和多个弱符号,使用强符号3.对于多个弱符号,随机选择一个。关于第一点,你在第一个例子中已经看到了。最常见的情况就是你重复定义变量或者函数等。第二点也有例子。例子中虽然定义了两个nums,但是var.c中未初始化的num是弱符号,main.c中的num是强符号。在这种情况下,编译是正常的。只是一个强签名的数字最终会被使用。这也类似于看第三个例子。main.c的num没有初始化的时候,也可以编译。这种情况下的误操作无非就是,如果是重复的符号,只是类型不同,问题就更大了,就是var.c的内容如下://var.cdoublenum;voidchange(){num=1023;}这里的num变成了double,再次编译运行,会发现意想不到的结果:before:numis1024after:numis0为什么修改后是0?原因是double类型和int类型的数据存储格式不同(参考《对浮点数的一些理解》),它们占用的空间长度不同。在本文的例子中,double占8个字节,而int占4个字节。总之,这不是我们想要的结果,最终的后果可能比我们想象的更严重,也更难发现。综上所述,如果没有特殊要求,应该尽量避免全局变量重名,以免出现意想不到的结果,比如使用变量时的最小作用域定义,即尽量避免使用全局变量,或者使用命名空间(例如在C++中)。当然,强弱符号在某些时候还是很有用的,比如做一个库支持用户自定义库,怎么办?请继续关注下一篇文章。参考参考书《深入理解计算机系统》《程序员的自我修养》