13.强符号和弱符号

news/2024/7/6 0:19:30

我们在编写代码的过程中经常会遇到一种叫做符号重复定义(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 被定义了两次,属于重复定义。

弱符号对于库来说十分有用,我们在开发库时,可以将某些符号定义为弱符号,这样就能够被用户定义的强符号覆盖,从而使得程序可以使用自定义版本的函数,增加了很大的灵活性。

本文转自:强符号和弱符号


http://www.niftyadmin.cn/n/1999232.html

相关文章

根号3136用计算机,根号1·100的平方根和立方根是多少

.算术平方根\x09立方根1\x091\x09 12\x091.414213562\x091.1224620483\x091.732050808\x091.2009369554\x092\x09 1.259921055\x092.236067977\x091.3076604866\x092.449489743\x091.3480061557\x092.645751311\x091.3830875548\x092.828427125\x091.4142135629\x093\x09 1.442…

RunTime运行时(一)

转自原文地址&#xff1a;http://blog.csdn.net/wzzvictory/article/details/8615569 作为一门动态编程语言&#xff0c;Objective-C 会尽可能的将编译和链接时要做的事情推迟到运行时。只要有可能,Objective-C 总是使用动态 的方式来解决问题。这意味着 Objective-C 语言不仅需…

武汉音乐学院计算机课,武汉音乐学院课程免修与自修管理规定(修订)

武汉音乐学院课程免修与自修管理规定(修订)武音教务〔2018〕15号为了鼓励学生自主学习&#xff0c;提高学习效率&#xff0c;实行人才培养目标&#xff0c;特制订本暂行规定。第一章 免 修第一条 学生通过学校组织的课程免修考试或在校期间参加相应的国家级考试&#xff0c;成绩…

delete this,dll导出类及如何写plugins

delete this,dll导出类及如何写plugins 1.为什么要使用delete this&#xff0c;以及如何使用。(1)考查这样的情况&#xff1a;有两个对象A,B需要访问同一个多态对象C。因为C一般是通过new 操作构造的&#xff0c;一定要自己释放&#xff0c;但是对象A,B都需要使用它&#xff0…

计算机系统的应用框架,[计算机软件及应用]软件框架.doc

[计算机软件及应用]软件框架HYPERLINK "/article/2013-10-15/2817185" \t "_blank" IT俱乐部创始人杜鸿飞专访 ?????? HYPERLINK "/survey/comein/613" \t "_blank" MDCC2013中国移动开发者大会社区赠票申请 ???? HYPERLINK…

Apache内存池内幕(1)

Apache内存池内幕(1) 对于APR中的所有的对象中&#xff0c;内存池对象应该是其余对象内存分配的基础&#xff0c;不仅是APR中的对象&#xff0c;而且对于整个Apache中的大部分对象的内存都是从内存池中进行分配的&#xff0c;因此我们将把内存池作为整个APR的基础。2.1 内存池…

Apache内存池内幕(2)

Apache内存池内幕(2) 2.3 内存池分配子allocator 2.3.1分配子概述 尽管我们可以通过malloc函数直接分配apr_memnode_t类型的结点&#xff0c;不过Apache中并不推荐这种做法。事实上Apache中的大部分的内存的分配都是由内存分配子allocator完成的。它隐藏了内部的实际的分配细节…

引导区坏 计算机无法启动,windows无法启动解决方法汇总

正常情况下&#xff0c;Windows未能正常启动&#xff0c;原因可能是最近更改了硬件或软件&#xff0c;由于排查系统问题是一个复杂的过程&#xff0c;并且硬件和软件配置可能有很大差别,因此可以采用许多方法解决Windows 无法正确启动。下面就给大家介绍windows无法启动的解决方…