在C语言中,强弱符号是与链接过程相关的重要概念,C++中不存在强弱符号,以下是对它们的具体讲解:
一、根本定义
- 强符号
强符号通常是指在编译单位(一般是一个源文件,即 .c 文件)中定义的全局变量大概函数。比方,在一个 .c 文件中定义了如下全局变量:
- int global_variable = 10; // 这是一个强符号的全局变量定义
复制代码 大概定义了这样的函数:
- int my_function(int a) {
- return a + 1;
- } // 这是一个强符号的函数定义
复制代码 强符号代表了一个确切的、具有唯一内存地点的实体,在链接阶段会参与到终极可执行文件的符号整合过程中。
- 弱符号
弱符号相对特别,它也是一种全局变量大概函数的体现形式,但允许在链接阶段被其他同名的强符号覆盖。在一些特定的应用场景中,比如在模块开辟中,大概一个模块提供了某个功能的弱符号实现,而另一个模块可以用更强的实现来替换它。一般未初始化的平凡全局变量 int g_val;
在常见的GCC编译器中,可以使用 __attribute__((weak)) 语法来声明一个弱符号。比方,声明一个弱符号的全局变量可以这样做:
- int __attribute__((weak)) weak_variable = 5; // 这是一个弱符号的全局变量声明及定义
复制代码 对于函数,同样可以定义为弱符号形式:
- int __attribute__((weak)) weak_function(int a) {
- return a;
- }
复制代码 二、链接过程中的举动
- 同名强符号辩论
当链接器在处置惩罚多个编译单位时,如果发现存在同名的强符号(比如两个不同的 .c 文件都定义了一个名为 same_name_variable 的全局变量且都是强符号形式),那么链接器会报错,提示符号重复定义(由于强符号都期望有唯一确定的定义进入终极可执行文件)。比方:
文件 file1.c 中有:
- int same_name_variable = 10;
复制代码 文件 file2.c 中有:
- int same_name_variable = 20;
复制代码 在链接这两个文件天生可执行文件时,链接器会指出 same_name_variable 重复定义的错误,无法继承天生可执行文件。
- 强符号与弱符号并存
如果一个符号既有强符号定义又有弱符号定义,在链接阶段,终极会使用强符号。比方,文件 file1.c 定义了强符号的全局变量:
- int combined_variable = 10;
复制代码 而文件 file2.c 定义了同名的弱符号全局变量:
- int __attribute__((weak)) combined_variable = 20;
复制代码 在链接这两个文件后,终极可执行文件中使用的是 file1.c 中定义的强符号对应的 combined_variable 的值(也就是 10),弱符号的定义被忽略了。
- 多个弱符号情况
当存在多个同名的弱符号时,链接器会选择其中一个(不同编译器大概不同链接器的具体选择策略大概略有差异,但一般是按照某种顺序选取,比如先遇到的那个等)一般会选择内存占用交大的那个,并且不会报错。比方,文件 file1.c 中有:
- int __attribute__((weak)) multi_weak_variable = 10;
复制代码 文件 file2.c 中有:
- int __attribute__((weak)) multi_weak_variable = 20;
复制代码 链接器大概会选择 file1.c 中定义的那个弱符号(假设按照遇到顺序选取等情况),并将其纳入终极可执行文件中,当然这种情况需要开辟者根据具体需求谨慎使用,避免出现不符合预期的结果。
三、应用场景
- 库函数的可选实现
在一些库的开辟中,可以将某些函数定义为弱符号。比如一个设备驱动库,对于某个特定功能的函数,在不同的硬件平台上大概有不同的实现方式,开辟者可以将底子的、通用的实现定义为弱符号,然后当针对具体硬件平台编译时,如果有更适配的强符号函数实现(比如针对该硬件专门优化过的函数),就可以覆盖弱符号对应的函数,这样能增强库的灵活性和可扩展性。
- 插件式开辟
在插件式的软件架构中,主步伐可以定义一些弱符号的函数大概变量,插件模块可以根据自身需求提供强符号的对应实现来覆盖主步伐中的弱符号,从而实现特定的功能扩展,同时又保证了在没有插件覆盖时主步伐也能有根本的、默认的运行状态。
总之,强弱符号在C语言中为步伐的模块化开辟、不同实现的灵活替换等方面提供了有用的机制,合理运用它们可以提升代码的可维护性和可扩展性。
四、强弱符号示例1
a.c
- int g_data1;
- void fun()
- {
- g_data1 = 20;
- }
复制代码 main.c
- #include<stdio.h>
- extern void fun();
- int g_data1=1;
- int main()
- {
- fun();
- printf("%d\n" , g_data1);
- return 0;
- }
复制代码 打印结果 20
分析如下
1、编译,编译阶段a.c fun()函数,g_data1 = 20;//往g_data1所在的内存空间写入数据20 , 写多大呢,由于编译阶段能看到本文件内g_data1为int 型 , 所以写四个字节;
2、链接阶段,由于a.c 里 g_data1只有声明,没有定义,所以是弱符号,平凡的、全局的、未初始化的变量,在编译阶段天生符号是放在一个特定的地区,在linux里是COM块 , 只有在链接后,进行强弱符号选择后,才会把符号放进.bss大概.data段 , 若符号在链接阶段会重新进行强弱符号的选择,终极选择强符号,也就是main.c里的g_data1 , 相当于往main.c的g_data1里写进20 ;
3、main.c编译阶段printf打印的就是强符号,所以打印出来就是20
五、稍有难度示例2
main.c
- #include<stdio.h>
- extern void fun();
- char gdata1=10;
- char gdata2 = 10;
- int main()
- {
- fun();
- printf("%d , %d\n" , gdata1 , gdata2);
- return 0;
- }
复制代码 a.c
- int gdata1;
- void fun()
- {
- gdata1 = 20;
- }
复制代码
分析如下:
1、编译阶段,a.c gdata1为弱符号,函数内gdata1 = 20;编译为往gdata1所在的内存空间写入20,由于编译阶段智能看到本文件内的符号,此时gdata1为Int 型 , 所以写入四个字节;
2、main.c在链接时,开始强弱符号选择,选择了强符号,强符号为char类型的,那就糟糕了,由于在编译阶段就已经确定了指令了,往gdata1所在内存写入四个字节,写入数据20,如下图,从gdata1开始往后四个字节写入20也表明0x 00 00 00 14 , 把gdata2的数据也给覆盖了,原本是10 , 0a,覆盖后变成了0 , 所以打印出来就是 20 ,0
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |