强符号和弱符号

我们在编写代码的过程中经常会遇到一种叫做符号重复定义(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)(个人:此时不管默认初始化)

强符号之所以强,是因为,它们拥有确切的数据

  • 变量有值,
  • 函数有函数体

弱符号之所以弱,是因为它们还未被初始化,没有确切的数据

链接器会按照如下的规则处理被多次定义的强符号和弱符号

  • 不允许强符号被多次定义,也即不同的目标文件中不能有同名的强符号如果有多个强符号,那么链接器会报符号重复定义错误。(个人:例如一个变量a被定义为int类型,int a =1;在另一个文件中被定义为double类型,double a = 1.00;即使类型不同,也算强符号多次定义)
  • 当仅有一个为强符号时,那么选择强符号

  • 如果一个符号在所有的目标文件中都是弱符号,那么选择其中占用空间最大的一个。比如目标文件 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)) int 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("www.baidu.com\n");
}

在 GCC 中,使用下面的命令来运行程序:  

    $gcc main.c module.c
    $./a.out
    a = 9999
    www.baidu.com
    

    在 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 被定义了两次,属于重复定义。

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

      

    posted on 2022-05-04 14:38  朴素贝叶斯  阅读(691)  评论(0编辑  收藏  举报

    导航