本文转载自微信公众号「编程珠玑」,函数或全会样作者守望先生。局变转载本文请联系编程珠玑(ID:shouwangxiansheng)公众号。量重 可能有些朋友第一反应是复定,那肯定是函数或全会样编译不过喽: 编译: 可以看到这里报错了,因为fun重复定义了。局变 但是量重重复定义就会报错,会编译不过吗?复定不全是! 再看下面的代码: 输出结果: 从结果中可以看到,虽然num被定义了两次,函数或全会样但是局变仍然可以编译通过,并且正常运行。量重这又是复定为什么呢? 符号 在说明今天重点分享的内容之前,先简单了解一下什么是函数或全会样符号。在《hello程序是局变如何变成可执行文件的》中讲到过,ELF文件生成的量重最后阶段会经历链接,而链接阶段正是基于符号才能完成。每个目标文件都会有一个符号表。而链接过程正是通过符号表中的符号,将不同的网站模板目标文件“粘”在一起,形成最后的库或者可执行文件。要查看一个目标文件的符号信息也很容易: 编译: 通过nm命令就可以查看符号信息,这里就有我们的func_symbol函数和全局变量symbol的符号。 关于nm的使用,在《几个命令了解ELF文件的秘密》也有介绍。 除了上面提到的全局符号,目标文件中还有其他符号信息,不过这不是本文关注的重点。 强符号与弱符号 对于C/C++语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。当然也可以通过 来定义一个强符号为弱符号。 通过下面的例子来看看哪些是强符号,哪些是弱符号: 注意,这里的亿华云强符号与弱符号都是针对定义来说的。 同名时,用哪个? 对于多重定义,即标题提到的变量重名时,链接器有它的处理规则: 关于第一点,在最开始的例子中你已经见到了,最常见的情况就是你重复定义了变量或者函数等等。 而第二点也有示例,示例中,虽然定义了两个num,但是var.c中未初始化的num是弱符号,main.c中的num是强符号,这种情况下编译正常。只是最终会使用强符号的num。 再看一个第三点的例子也是类似,服务器托管当其中main.c的num无初始化时,也是可以编译过的。这种情况下的误用也就罢了,如果是重复的符号,但是类型不同,问题就更大了,即var.c的内容如下: 这里的num变成了double,再次编译运行,你会发现意想不到的结果: 为什么修改后是0?原因在于double类型的数据存储与int类型数据存储格式不一样(参考《对浮点数的一些理解》),且它们占用空间长度都不一样,在本文例子中,double占用8字节,而int占用4字节。 总之,这不是我们想要的结果,最终的后果可能比我们想象的要严重,要更难发现。 总结 如非特殊需求,应该尽量避免出现全局变量同名,以免造成意料不到的结果,例如使用变量时最小范围定义,即尽可能避免全局变量,或者使用命名空间(如C++中)。 当然了,强弱符号在某些时候是非常有用的,例如制作库以支持用户自定义的库,这又该怎么做呢?敬请期待下一篇。 参考 参考书籍 《深入理解计算机系统》 《程序员的自我修养》