前言:前面的文章中,我们详细介绍了c++中的纯虚函数,作者联想到c语言中的一个思想与c++的纯虚函数有异曲同工之妙,那就是弱定义。弱定义这个概念,可能做嵌入式开发的童鞋接触的会比较多,本文跟大家一起来学习一下。
一、弱定义的概念
c语言弱定义是通过特定修饰符声明函数或变量的技术,本质是上是一套链接期的符号决议规则:同一个全局符号(函数或变量)允许出现多份定义,其中最多只有一份“强定义”,其余都是“弱定义”。在链接过程中优先选择强定义的符号,而弱定义仅在没有强定义时被使用。
弱定义特点:
- 允许符号在链接时被覆盖
- 提供默认实现,可被强定义替换
- 常用于库函数的可覆盖实现
- 在gcc中通过 __attribute__((weak)) 声明
__attribute__((weak))
二、弱定义与强定义区别
特性 | 强定义 | 弱定义 |
符号类型 | t (text), d (data) | w (weak) |
可覆盖性 | 不可被覆盖 | 可被强定义覆盖 |
多重定义 | 导致链接错误 | 允许存在多个定义 |
内存占用 | 一定会被编译进最终二进制文件(.elf/.hex) | 不一定。若有强定义覆盖,则不会被编译进去 |
使用场景 | 普通函数/变量 | 默认实现、钩子函数等 |
| 优先级 | 最高 | 最低 |
| 形象比喻 | 正式工 | 临时工/备胎 |
三、弱定义语法
3.1 使用__attribute__((weak))
// 弱定义一个函数
__attribute__((weak)) void weak_function(void) {
printf("这是弱定义函数\n");
}
// 弱定义一个变量
__attribute__((weak)) int weak_var = 10;
3.2 使用#pragma weak
#pragma weak funca
void funca() {
// ... 默认行为
}一般普遍使用的是第一种格式。
四、链接过程
链接器在解析符号时遵循以下规则:
- 如果只有一个强定义,选择该定义
- 如果有多个强定义,报告多重定义错误
- 如果有一个弱定义和一个强定义,选择强定义
- 如果有多个弱定义,任选一个(通常是第一个遇到的)

看到这里,大家有没有觉得弱定义的这种机制,是不是有点像c++中的虚函数/纯虚函数:基类提供“默认实现”的虚函数/纯虚函数,派生类重写来“覆盖”基类的默认行为。(两者的相似性,童鞋们可以细细品味)
五、代码示例 — 弱函数的定于与覆盖
1)定义并使用弱函数:
// weak_example.c
#include <stdio.h>
// 弱定义函数
__attribute__((weak)) void my_function(void) {
printf("默认的弱定义函数被调用\n");
}
int main(void) {
my_function(); // 调用函数
return 0;
}
2)编译运行:
$ gcc weak_example.c -o weak_example $ ./weak_example 默认的弱定义函数被调用
3)强定义覆盖:
// strong_override.c
#include <stdio.h>
// 普通的强定义函数
void my_function(void) {
printf("强定义函数覆盖\n");
}
4)编译运行weak_example.c与strong_overrid.c文件:
$ gcc weak_example.c strong_override.c -o override_example $ ./override_example 强定义函数覆盖
六、典型使用场景
6.1 库的默认实现
在开发库时,可以使用弱定义提供函数的默认实现,允许用户通过提供强定义来覆盖默认行为。好处:库保持默认行为,用户无需修改库源码即可定制功能。
6.2 中断处理函数
在嵌入式系统中,默认中断处理函数通常定义为弱定义,允许应用程序根据需要提供特定的中断处理实现。好处:避免未定义中断导致崩溃,且支持用户灵活定制中断处理逻辑。
到此这篇关于浅谈c语言弱定义机制的文章就介绍到这了,更多相关c语言弱定义内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论