C 语言弱链接函数 __weak
- C语言进阶
- 13小时前
- 29热度
- 0评论
1. 什么是弱链接函数
__weak 修饰的函数属于弱符号,用于提供可被覆盖的默认实现。当链接时发现同名的强符号函数,链接器会优先使用强符号,弱符号被忽略。
ℹ️ 典型应用:HAL 库提供空的弱回调函数,用户在自己的代码中实现同名函数即可覆盖,无需修改库代码。
2. 语法格式
GCC 原生语法是 __attribute__((weak)),通常会自定义宏 __weak 来简化使用:
#define __weak __attribute__((weak))
__weak void function_name(void) {
// 默认实现
}
💡 编译器差异
• ARM GCC / Keil MDK 内置支持
• 标准 GCC 需要自己定义宏
• ARM GCC / Keil MDK 内置支持
__weak• 标准 GCC 需要自己定义宏
#define __weak __attribute__((weak))
3. 基础示例
00weak_default.c - 提供默认实现
#include <stdio.h>
#define __weak __attribute__((weak))
// 弱符号:默认配置
__weak void config(void) {
printf("默认配置\n");
}
00weak_user.c - 用户覆盖实现
#include <stdio.h>
// 强符号:覆盖默认配置
void config(void) {
printf("配置一\n");
}
int main(void) {
config(); // 输出:配置一
return 0;
}
编译运行:
gcc 00weak_default.c 00weak_user.c -o test
./test
# 输出:配置一
4. 工作原理

4.1 编译阶段:打标记
- 编译器看到
__weak时,给符号打上「弱标记」 - 同一个
.c文件内不能有多个同名函数(无论是否 weak),否则报重定义错误 - 此阶段不处理覆盖逻辑
4.2 链接阶段:看标记
- 链接器识别符号的弱标记(用
nm命令可看到W标记) - 按规则处理:强符号 > 弱符号
- 多个强符号会报重定义错误

📋 核心机制
「编译打标记,链接看标记;weak 的作用全在链接阶段」
「编译打标记,链接看标记;weak 的作用全在链接阶段」
5. 嵌入式示例:STM32 GPIO 中断回调
stm32f4xx_hal_gpio.c - HAL 库提供弱实现
// HAL 库提供空的弱回调函数
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
UNUSED(GPIO_Pin);
// 用户应该在自己的代码中实现此函数
}
// GPIO 中断服务函数(HAL库内部)
void EXTI0_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
// 内部会调用 HAL_GPIO_EXTI_Callback
}
main.c - 用户实现强符号
// 用户实现 GPIO 中断回调(强符号覆盖弱符号)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if(GPIO_Pin == GPIO_PIN_0) {
// 按键中断处理
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // LED 翻转
}
}
结果:链接时使用 main.c 中的强符号,HAL 库的弱符号被忽略。
6. 注意事项
6.1 ❌ 同文件内定义多个同名函数
// error.c
__weak void func(void) { }
void func(void) { } // ❌ 编译错误:重定义
⚠️ 注意:这不是 weak 失效,而是 C 语言基础规则——单文件不允许同名函数。
6.2 ❌ 函数签名不匹配
// lib.c
__weak void Init(void) { }
// main.c
void Init(int param) { } // ❌ 签名不同,链接时报错
⚠️ 注意:C 语言不支持函数重载,同名不同参数的函数会导致符号冲突。即使一个是弱符号,链接器也无法正确处理,会报错或产生未定义行为。
6.3 ✅ 必须在不同源文件
// lib.c
__weak void Init(void) { printf("default\n"); }
// main.c
void Init(void) { printf("custom\n"); } // ✅ 正确覆盖
7. 总结
📋 速记要点
__weak用于提供可覆盖的默认实现- 必须在不同 .c 文件中才能覆盖
- 函数签名必须完全一致
- 常用于 HAL 库回调、RTOS 钩子函数、驱动框架