在 C 语言(实际上是 GCC/LD 链接器相关特性)中,弱符号(weak symbol) 是一种允许 符号(函数或变量)被覆盖或可选定义 的机制。它主要用于库函数、驱动程序、操作系统内核或者需要可替换实现的场景。

下面给你详细讲解 弱符号的原理、用法和示例


1. 弱符号概念

  • 普通符号(strong symbol):在链接时,如果有多个同名符号,会导致链接错误。
  • 弱符号(weak symbol)
    • 可以被 同名强符号覆盖
    • 如果没有强符号定义,则使用弱符号。
    • 如果既没有强符号,也没有弱符号,链接报错(未定义符号)。

特点:

  • 用于提供默认实现。
  • 支持可选覆盖。
  • 链接器会优先选择强符号。

2. GCC 弱符号声明方法

在 GCC 中,可以使用 __attribute__((weak)) 声明一个符号为弱符号。

语法:

int var __attribute__((weak)) = 10;      // 弱变量
void func() __attribute__((weak));       // 弱函数声明

注意:

  • 也可以使用 #pragma weak func,不过 __attribute__((weak)) 更常用。

3. 弱符号的使用场景

  1. 提供默认实现
    • 库函数提供默认实现,如果用户需要自定义,则定义同名函数覆盖默认实现。
  2. 内核或驱动程序中可选功能
    • 在 Linux 内核中常用于可选驱动或模块函数。
  3. 条件编译或兼容旧版本
    • 兼容某些不存在的函数或变量,不会导致链接错误。

4. 弱符号示例

示例 1:弱函数覆盖

#include <stdio.h>

// 默认实现(弱符号)
void greet(void) __attribute__((weak));
void greet(void) {
    printf("Hello from weak function!\n");
}

int main() {
    greet(); // 如果没有强函数定义,会调用弱函数
    return 0;
}

覆盖弱函数(强符号)

在同一个项目中定义一个同名强函数:

#include <stdio.h>

// 强函数实现
void greet(void) {
    printf("Hello from strong function!\n");
}

编译并链接:

gcc main.c strong_greet.c -o test
./test

输出:

Hello from strong function!

链接器优先选择强符号,弱符号被覆盖。


示例 2:弱变量覆盖

#include <stdio.h>

int value __attribute__((weak)) = 42;  // 弱变量

int main() {
    printf("value = %d\n", value);
    return 0;
}

覆盖弱变量

int value = 100; // 强变量,覆盖弱变量

结果:

value = 100


示例 3:条件编译使用弱符号

#include <stdio.h>

// 弱函数提供默认实现
void optional_feature(void) __attribute__((weak));
void optional_feature(void) {
    printf("Default optional feature\n");
}

int main() {
    if (optional_feature) {
        optional_feature();  // 如果没有覆盖实现,也不会崩溃
    }
    return 0;
}

if (optional_feature) 检查函数指针是否为 NULL。如果用户未定义强函数实现,弱函数会被调用。


5. 总结

特性强符号(strong)弱符号(weak)
链接优先级
可被覆盖
未定义时链接错误链接错误(没有弱符号也未定义)
使用场景普通函数、变量默认实现、可选功能、可替换实现

弱符号特点

  • 可以作为 默认实现
  • 可以被 同名强符号覆盖
  • 支持可选功能或模块化设计。