__attribute__((weak)) 是 GCC 和 Clang 提供的一个特性,用于声明一个符号为 "弱符号"(weak symbol),在链接层面提供更多的灵活性。注意并不是 C 语言标准的一部分,MSVC并不支持,但是提供了一个类似的特性:#pragma weak

概述

编译器将一个符号(函数名,变量名)区分为强符号(strong symbol)和弱符号(weak symbol),通常绝大多数的符号都是强符号,重复出现会导致链接错误。在定义时使用 __attribute__((weak)) 可以将其设置为弱符号,语法如下(在符号声明时不需要)

1
<符号类型> __attribute__((weak)) <符号名称>;

这可以使得它在链接过程中具有较低的优先级:

  • 如果在链接过程中找到了同名的强符号和弱符号,弱符号会被强符号覆盖。
  • 如果找到多个同名的强符号,会导致链接错误(link error)。
  • 在没有找到强符号时:如果只有唯一的弱符号,那么这个弱符号会被使用;如果存在多个同名的弱符号,可能会使用其中一个版本,取决于具体实现,并且很可能和链接顺序有关,通常会使用第一个或最后一个版本。

因此使用弱符号可以在库中给函数提供一个默认实现,同时允许用户提供自己的实现(作为强符号,覆盖默认实现), 这可以让用户在不改变库代码的前提下,在不同平台或配置环境中使用不同的实现版本。

实例

假设我们在某个库中定义了一个函数 foo,并且希望用户可以根据需求选择是否重定义它。

库的内容如下

1
2
3
4
5
#include <stdio.h>

void __attribute__((weak)) foo() {
printf("This is the default foo implementation.\n");
}

如果用户没有定义,就使用默认的 foo 实现

1
2
3
4
5
6
7
8
#include <stdio.h>

extern void foo();

int main() {
foo();
return 0;
}

否则使用用户定义的 foo

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>

extern void foo();

void foo() {
printf("This is the user-defined foo implementation.\n");
}

int main() {
foo();
return 0;
}

编译为动态库后分别调用,就可以得到不同的结果

1
2
gcc -fPIC -shared foo.c -o libfoo.so
gcc main.c -lfoo -L. -o main

使用静态库也是一样的,不影响这里的结果。