13.强符号和弱符号
我们在编写代码的过程中经常会遇到一种叫做符号重复定义(Multiple Definition)的错误,这是因为在多个源文件中定义了名字相同的全局变量,并且都将它们初始化了。
例如,在 a.c 中定义了全局变量 global:
int global = 10;
在 b.c 中又对 global 进行了定义:
int global = 20;
那么在链接时就会出现下面的错误:
b.o: multiple definition of `global'
a.o: first defined here
这种符号的定义可以被称为强符号。
在C语言中,编译器默认函数和初始化了的全局变量为强符号(Strong Symbol),未初始化的全局变量为弱符号(Weak Symbol)。强符号之所以强,是因为它们拥有确切的数据,变量有值,函数有函数体;弱符号之所以弱,是因为它们还未被初始化,没有确切的数据。
链接器会按照如下的规则处理被多次定义的强符号和弱符号:
1) 不允许强符号被多次定义,也即不同的目标文件中不能有同名的强符号;如果有多个强符号,那么链接器会报符号重复定义错误。
2) 如果一个符号在某个目标文件中是强符号,在其他文件中是弱符号,那么选择强符号。
3) 如果一个符号在所有的目标文件中都是弱符号,那么选择其中占用空间最大的一个。
比如目标文件 a.o 定义全局变量 global 为 int 类型,占用4个字节,目标文件 b.o 定义 global 为 double 类型,占用8个字节,那么被链接后,符号 global 占用8个字节。请尽量不要使用多个不同类型的弱符号,否则有时候很难发现程序错误。
在 GCC 中,可以通过__attribute__((weak))
来强制定义任何一个符号为弱符号。假设现在有下面的一段代码:
extern int ext; int weak1; int strong = 100; __attribute__((weak)) weak2 = 2; int main(){ return 0; }
weak1 和 weak2 是弱符号,strong 和 main 是强符号,而 ext 既非强符号也非弱符号,它是一个对外部变量的引用(使用)。
为了加深理解,我们不妨再来看一个多文件编程的例子。
main.c 源码:
#include <stdio.h> //弱符号 __attribute__((weak)) int a = 20; __attribute__((weak)) void func(){ printf("C Language\n"); } int main(){ printf("a = %d\n", a); func(); return 0; }
module.c 源码:
#include <stdio.h> //强符号 int a = 9999; void func(){ printf("c.biancheng.net\n"); }
在 GCC 中,使用下面的命令来运行程序:
$gcc main.c module.c $./a.out a = 9999 c.biancheng.net
在 main.c 中,a 和 func 都是弱符号,在 module.c 中,a 和 func 都是强符号,强符号会覆盖弱符号,所以链接器最终会使用 module.c 中的符号,输出结果也印证了这一点。
需要注意的是,__attribute__((weak))
只对链接器有效,对编译器不起作用,编译器不区分强符号和弱符号,只要在一个源文件中定义两个相同的符号,不管它们是强是弱,都会报“重复定义”错误。请看下面代码:
#include <stdio.h> __attribute__((weak)) int a = 20; int a = 9999; int main(){ printf("a = %d\n", a); return 0; }
这段代码在编译阶段就会报错,编译器会认为变量 a 被定义了两次,属于重复定义。
弱符号对于库来说十分有用,我们在开发库时,可以将某些符号定义为弱符号,这样就能够被用户定义的强符号覆盖,从而使得程序可以使用自定义版本的函数,增加了很大的灵活性。
本文转自:强符号和弱符号
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
2016-12-22 QQ空间HD(4)-设置左侧菜单栏属性
2016-12-22 QQ空间HD(3)-Modal的切换效果总结
2016-12-22 QQ空间HD(2)-UIPopoverController其它使用
2016-12-22 iOS- Could not find a storyboard named 'Main' in bundle NSBundle