上篇文章:ARM 嵌入式 编译系列 2.1 – GCC 编译参数学习
下篇文章: ARM 嵌入式 编译系列 3.1 – GCC attribute((used)) 使用
GCC attribute((weak)) 介绍
弱符号,这涉及到编译中符号的概念。在Linux开发环境中,有强符号和弱符号,符号简单来说就是函数、变量的名字,对于全局(非局部、非static
)的函数和变量,能不能重名是有一定规矩的,强、弱符号就是针对这些全局函数和变量来说的。
符号类型 | 对象 |
---|---|
强 | 函数名,赋初值的全局变量 |
弱 | 未初始化的全局变量 |
当代码中同时存在多个强或弱的全局变量时,要遵守如下规则:
- 强符号只能定义一次,否则编译错误;
- 强弱符号同时存在,以强符号为准;
- 没有强符号,则从多个弱符号中任选一个,用
–fno-common
编译选项可以在这种情况下打出 warning。
弱符号的声明
弱符号的声明有两种方式
第一种,用 __attribute__((weak))
修饰,例如:
void __attribute__((weak)) func(void);
extern int __attribute__((weak)) var; //直接声明为弱函数
第二种,用 #pragma weak
标记,例如:
#pragma weak func
代码演示
main.c
这个文件中 main
函数调用了 2 个 声明为弱符号的函数,它们是 weak0
和weak1
:
#include <stdio.h>
void __attribute__((weak)) weak0(void);
void __attribute__((weak)) weak1(void);
int main(int argc, char **argv) {
/* 尝试调用弱符号函数 weak0 */
if (weak0) {
weak0();
} else {
printf("weak0=%p\n", weak0);
}
/* 尝试调用弱符号函数 weak1 */
if (weak1) {
weak1();
} else{
printf("weak1=%p\n", weak1);
}
return 0;
}
weak.c
这个文件中定义了2个函数(它们还是weak0
和weak1
),并强制声明为弱符号:
#include <stdio.h>
/* 标记 weak0 为弱符号 */
#pragma weak weak0
/* 标记 weak1 为弱符号 */
void __attribute__((weak)) weak1(void);
static char *label = "weak";
void weak0(void) {
printf("[%s]%s is called\n", label, __FUNCTION__);
}
void weak1(void) {
printf("[%s]%s is called\n", label, __FUNCTION__);
}
stong.c
重复定义两个强函数:void weak0(void)
和 void weak1(void)
:
#include <stdio.h>
static char *label = "strong";
void weak0(void) {
printf("[%s]%s is called\n", label, __FUNCTION__);
}
void weak1(void) {
printf("[%s]%s is called\n", label, __FUNCTION__);
}
编译与输出
编译 gcc main.c strong.c weak.c
弱符号链接成功时,可以被正常调用。
当强符号定义出现时,弱符号定义不起作用,打印出来的是强函数的内容:
$ ./a.exe
[strong]weak0 is called
[strong]weak1 is called
GCC 编译参数 -fno-common
在GCC中,-fno-common
编译选项的作用是把定义在不同源文件中的同名全局变量视为多重定义(multiple defined)。
通常情况下,GCC允许你在多个C文件中定义同名的全局变量(不带static),GCC会把它们视为同一个变量。例如,如果你在"file1.c
"中定义了一个全局变量int x
;,然后在"file2.c
"中又定义了一个同名的全局变量int x
;,正常情况下GCC会把它们视为同一个变量。
但是,如果你开启了-fno-common
选项,GCC就会把这种情况视为错误。也就是说,如果你在"file1.c
"和"file2.c
"中都定义了全局变量int x
;,并且使用-fno-common
编译,GCC将会报错,提示x被多重定义。
-fno-common
选项可以帮助检测到可能的错误。例如,如果你在两个不同的源文件中无意中使用了同样的全局变量名,而这两个变量应该是不同的,那么-fno-common
可以帮助发现这个问题。
在实际编程中,我们应该尽量避免在不同的源文件中定义同名的全局变量,以减少可能的错误和混淆。对于需要在多个源文件中共享的全局变量,我们应该在一个源文件中定义它,然后在其他源文件中使用extern
来声明它。
上篇文章:ARM 嵌入式 编译系列 2.1 – GCC 编译参数学习
下篇文章: ARM 嵌入式 编译系列 3.1 – GCC attribute((used)) 使用