c进阶篇(十)——weak用法详解
前言:
weak用于修饰弱函数或变量,以实现类似c++语言中函数重载的功能,本篇文章详细探讨weak的运用。
1 什么是weak函数
weak
不是c语言的关键字,而是一些编译器的扩展属性,MDK-ARM中在函数定义和声明前加 __attribute__((weak))
表示将该函数定义为弱函数,而在变量定义或声明前加 __attribute__((weak))
表示将该变量定义为弱变量。弱函数或变量允许多次定义,编译器不会报重复定义的错误,但是调用时只能有一个有效。编译器会根据强弱程度决定调用哪个定义,简单来说就是支持重定义。普通定义的优先级大于弱定义,具体的调用规则如下。
- 一次或多次弱定义,一次普通定义。编译器忽略所有弱定义,调用普通定义;
- 一次或多次弱定义,无普通定义。编译器会发出警告,会调用其中一个弱定义,具体调用哪一个不确定;
- 普通定义不能有多次,否则报错重复定义;
注意,weak不能与static搭配,因此不存在静态局部弱函数或变量,weak只能修饰全局函数或变量。
2 weak修饰函数
2.1 weak修饰函数定义
weak对函数定义进行修饰时,该函数定义即为弱定义。以下代码演示在函数定义处进行weak修饰。
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "hdr.h"
__attribute__((weak)) byte Fun(void) { return 1u; }
int32_t main(void) { byte byTmp = Fun();
while(1u); }
|
1 2 3 4 5 6
| #include "hdr.h"
byte Fun(void) { return 2u; }
|
这段代码在hdr.h中声明了全局函数 Fun
,在main.c中对该函数进行了弱定义并且返回值为 1u
,在com.c中重复定义了该函数并且返回值为 2u
,调试运行后观察到 Fun
函数的返回值为 2
。虽然有两次定义但其中一次为弱定义,一次为普通定义,这两种定义同时存在时,编译器会调用普通定义,弱定义将会失效,也即普通定义优先级高于弱定义。
不论是弱定义还是普通定义,同一个函数在一个.c文件中只能定义一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "hdr.h"
__attribute__((weak)) byte Fun(void) { return 1u; }
__attribute__((weak)) byte Fun(void) { return 2u; }
int32_t main(void) { byte byTmp = Fun();
while(1u); }
|
以上代码存在错误,在同一个.c文件中对同一个函数进行了多次定义(弱定义)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "hdr.h"
__attribute__((weak)) byte Fun(void) { return 1u; }
byte Fun(void) { return 2u; }
int32_t main(void) { byte byTmp = Fun();
while(1u); }
|
以上代码存在错误,在同一个.c文件中对同一个函数进行了多次定义(一次弱定义和一次普通定义)。
2.2 weak修饰函数声明
weak也可以用来修饰函数声明。
1
| extern __attribute__((weak)) byte Fun(void);
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "hdr.h"
byte Fun(void) { return 1u; }
int32_t main(void) { byte byTmp = Fun();
while(1u); }
|
以上代码在.h文件中对函数声明使用 __attribute__((weak))
修饰,并且在.c中对该函数普通定义,这样代码是合法的。若在多个文件.h中进行带weak修饰的函数声明,则编译器会给出警告,见以下代码。
1 2 3 4
| #include "com.h" #include "other.h"
extern __attribute__((weak)) byte Fun(void);
|
1
| extern __attribute__((weak)) byte Fun(void);
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include "hdr.h"
byte Fun(void) { return 1u; }
int32_t main(void) { byte byTmp = Fun();
while(1u); }
|
以上代码是正确的,可以看出在至少有一个weak修饰函数声明的情况下,增加普通的函数声明是合法的。
2.3 weak同时修饰函数定义和声明
weak同时修饰函数定义和声明时,还有分几种情况讨论。这种方式不推荐。
1 2 3
| #include "com.h"
extern __attribute__((weak)) byte Fun(void);
|
1 2 3
| #include "hdr.h"
extern __attribute__((weak)) byte Fun(void);
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| #define "hdr.h"
__attribute__((weak)) byte Fun(void) { return 1u; }
int32_t main(void) { byte byTmp = Fun();
while(1u); }
|
当只有一次函数弱定义,一个及以上函数弱声明可都或部分被 __attribute__((weak))
修饰,这时候编译器是没有报错和警告的。
1
| extern __attribute__((weak)) byte Fun(void);
|
1 2 3 4
| #include "hdr.h"
extern __attribute__((weak)) byte Fun(void); extern byte Fun(void);
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| #define "hdr.h"
__attribute__((weak)) byte Fun(void) { return 1u; }
int32_t main(void) { byte byTmp = Fun();
while(1u); }
|
1 2 3 4 5 6
| #include "hdr.h"
__attribute__((weak)) byte Fun(void) { return 2u; }
|
当有多次弱定义和声明(可含有普通声明)而无普通定义时,编译器会发出警告,会调用其中一个弱定义,具体调用哪一个不确定。
1
| __attribute__((weak)) extern byte Fun(void);
|
1 2 3 4 5 6
| #include "hdr.h"
__attribute__((weak)) byte Fun(void) { return 1u; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| #define "hdr.h"
__attribute__((weak)) byte Fun(void) { return 2u; }
int32_t main(void) { byte byTmp = Fun();
while(1u); }
|
当同时对函数定义和声明用 __attribute__((weak))
进行修饰时,如果此时还有一个普通定义,则会产生警告。
3 weak修饰变量
3.1 weak修饰变量定义
weak对变量定义进行修饰时,该变量定义即为弱定义。以下代码演示在变量定义处进行weak修饰。
1 2 3 4 5 6 7 8 9 10
| #include "hdr.h"
__attribute__((weak)) byte g_byTmp = 1u;
int32_t main(void) { byte byTmp = g_byTmp;
while(1u); }
|
1 2 3
| #include "hdr.h"
byte g_byTmp = 2u;
|
这段代码在hdr.h中声明了全局变量 g_byTmp
,在main.c中对该变量进行了弱定义并且初始化值为 1u
,在com.c中重复定义了该变量并且初始化值为 2u
,调试运行后观察到 g_byTmp
变量的值为 2
。虽然有两次定义但其中一次为弱定义,一次为普通定义,这两种定义同时存在时,编译器会调用普通定义,弱定义将会失效,也即普通定义优先级高于弱定义。
不论是弱定义还是普通定义,同一个变量在一个.c文件中只能定义一次。
1 2 3 4 5 6 7 8 9 10 11
| #include "hdr.h"
__attribute__((weak)) byte g_byTmp = 1u; __attribute__((weak)) byte g_byTmp = 2u;
int32_t main(void) { byte byTmp = g_byTmp;
while(1u); }
|
以上代码存在错误,在同一个.c文件中对同一个变量进行了多次定义(弱定义)。
1 2 3 4 5 6 7 8 9 10 11
| #include "hdr.h"
__attribute__((weak)) byte g_byTmp = 1u; g_byTmp = 2u;
int32_t main(void) { byte byTmp = g_byTmp;
while(1u); }
|
以上代码存在错误,在同一个.c文件中对同一个变量进行了多次定义(一次弱定义和一次普通定义)。
3.2 weak修饰变量声明
weak也可以用来修饰变量声明。
1
| extern __attribute__((weak)) byte g_byTmp;
|
1 2 3 4 5 6 7 8 9 10
| #include "hdr.h"
byte g_byTmp = 1u;
int32_t main(void) { byte byTmp = g_byTmp;
while(1u); }
|
以上代码在.h文件中对变量声明使用 __attribute__((weak))
修饰,并且在.c中对该变量普通定义,这样代码是合法的。若在多个文件.h中进行带weak修饰的变量声明,则编译器会给出警告,见以下代码。
1 2 3 4
| #include "com.h" #include "other.h"
extern __attribute__((weak)) byte g_byTmp;
|
1
| extern __attribute__((weak)) byte g_byTmp;
|
1 2 3 4 5 6 7 8 9 10
| #include "hdr.h"
byte g_byTmp = 1u;
int32_t main(void) { byte byTmp = g_byTmp;
while(1u); }
|
以上代码是正确的,可以看出在至少有一个weak修饰变量声明的情况下,增加普通的变量声明是合法的。
3.3 weak同时修饰变量定义和声明
weak同时修饰变量定义和声明时,还有分几种情况讨论。这种方式不推荐。
1 2 3
| #include "com.h"
extern __attribute__((weak)) byte g_byTmp;
|
1 2 3
| #include "hdr.h"
extern __attribute__((weak)) byte g_byTmp;
|
1 2 3 4 5 6 7 8 9 10
| #define "hdr.h"
__attribute__((weak)) byte g_byTmp = 1u;
int32_t main(void) { byte byTmp = g_byTmp;
while(1u); }
|
当只有一次变量弱定义,一个及以上变量弱声明可都或部分被 __attribute__((weak))
修饰,这时候编译器是没有报错和警告的。
1
| extern __attribute__((weak)) byte g_byTmp;
|
1 2 3 4
| #include "hdr.h"
extern __attribute__((weak)) byte g_byTmp; extern byte g_byTmp = 1u;
|
1 2 3 4 5 6 7 8 9 10
| #define "hdr.h"
__attribute__((weak)) byte g_byTmp = 1u;
int32_t main(void) { byte byTmp = g_byTmp;
while(1u); }
|
1 2 3
| #include "hdr.h"
__attribute__((weak)) byte g_byTmp = 2u;
|
当有多次弱定义和声明(可含有普通声明)而无普通定义时,编译器会发出警告,会调用其中一个弱定义,具体调用哪一个不确定。
1
| __attribute__((weak)) byte g_byTmp = 1u;
|
1 2 3
| #include "hdr.h"
byte g_byTmp = 4u;
|
1 2 3 4 5 6 7 8 9 10
| #define "hdr.h"
__attribute__((weak)) byte g_byTmp = 3u;
int32_t main(void) { byte byTmp = g_byTmp;
while(1u); }
|
当同时对变量定义和声明用 __attribute__((weak))
进行修饰时,如果此时还有一个普通定义,则会产生警告。