【ARM 嵌入式 C 入门及渐进 3 -- GCC __attribute__((weak)) 弱符号使用】


请阅读【ARM GCC 编译专栏导读】


上篇文章: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 个 声明为弱符号的函数,它们是 weak0weak1

#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个函数(它们还是weak0weak1),并强制声明为弱符号:

#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)) 使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

主公讲 ARM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值