弱符号与强符号 vs 弱引用与强引用

发布时间 2023-10-29 14:58:50作者: zephyr~

弱符号与强符号

说明

对于C/C++语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。我们也可以通过GCC的"__attribute__((weak))"来定义任何一个强符号为弱符号。注意,强符号和弱符号都是针对定义来说的,不是针对符号的引用。
比如我们有下面这段程序:

extern int ext;
int weak;
int strong = 1;
_attribute_((weak))weak2=2;

int main()
{
    return 0;
}

上面这段程序中,"weak"和"weak2"是弱符号,"strong"和"main"是强符号,而"ext"既非强符号也非弱符号,因为它是一个外部变量的引用。针对强弱符号的概念,链接器就会按如下规则处理与选择被多次定义的全局符号:

  • 规则1:不允许强符号被多次定义(即不同的目标文件中不能有|同名的强符号):如果
    有多个强符号定义,则链接器报符号重复定义错误。

  • 规则2:如果一个符号在某个目标文件中是强符号,在其他文件中中都是弱符号,那么选
    择强符号。

  • 规则3:如果一个符号在所有目标文件中都是弱符号,那么选择其中占用空间最大的一
    个。比如目标文件A定义全局变量global为int型,占4个字节;目标文件B定义global
    为double型,占8个字节,那么目标文件A和B链接后,符号global占8个字节(尽)
    量不要使用多个不同类型的弱符号,否则容易导致很难发现的程序错误)。

使用场景

比如系统可以预定义一个弱符号的函数,或者弱符号的变量。如果用户想自己修改。那么用户的只需要定义对应的强符号,就会覆盖系统的默认行为。

弱引用与强引用

说明

目前我们所看到的对外部目标文件的符号引用在目标文件被最终链接成可执行文件时,它们须要被正确决议,如果没有找到该符号的定义,链接器就会报符号未定义错误,这种被称为强引用(StrongReference)。与之相对应还有一种弱引用(WeakReference),在处理弱引用时,如果该符号有定义,则链接器将使用该符号的引用决议:如果该符号未被定义,则链接器对于该引用不报错
链接器处理强引用和弱引用的过程几乎一样,只是对于未定义的弱引用,链接器不认为它是一个错误。一般对于未定义的弱引用,链接器默认其为0,或者是一个特殊的值,以便于程序代码能够识别。弱引用和弱符号主要用于库的链接过程,我们将在"库"这一章再来详细讲述。

__attribute__((weakref)) void foo();
int main()
{
   if (foo)
       foo();
}

我们可以将它编译成一个可执行文件,GCC并不会报链接错误。但是当我们运行这个可执行文件时,会发生运行错误。因为当main函数试图调foo函数时,foo函数的地址为
0,于是发生了非法地址访问的错误。

使用场景

这种弱符号和弱引用对于库来说十分有用,比如库中定义的,弱符号可以被用户定义的强符号所覆盖,从而使得程序可以使用自定义版本的库函数;或者程序可以对某些扩展功能模块的引用定义为弱引用,当我们将扩展模块与程序链接在一起时,功能模块就可以正常使用;如果我们去掉了某些功能模块,那么程序也可以正常链接,只是缺少了相应的功能,这使得程序的功能更加容易裁剪和组合。