-
2018-06-20 11:28:33__weak是一个宏,和__packed是同一种东西都是gcc的扩展属性:
#define __packed __attribute__((packed))
#define __weak __attribute__((weak))
如果这个关键字用在函数定义上面,一般情况下和一般函数没有两样。但是当有一个同名函数但是不带__weak被定义时,所有对这个函数的调用都是指向后者(不带__weak那个), 如果有两个一样的函数都用了__weak,那么真正调用那个,就要看连接器了。
原文地址:https://blog.csdn.net/adaptiver/article/details/6695637
更多相关内容 -
c语言中weak的作用
2021-05-25 00:55:08c语言中weak的作用转载参考至:https://blog.csdn.net/q251900...在u-boot源码中看到__weak关键字,在移植过程中遇到了问题。用例weak在不同的环境中用法不同,在stm32源码中也比较常见,在u-boot和linux中用法都不...c语言中weak的作用
转载参考至:https://blog.csdn.net/q251900...
在u-boot源码中看到__weak关键字,在移植过程中遇到了问题。
用例
weak在不同的环境中用法不同,在stm32源码中也比较常见,在u-boot和linux中用法都不一样。
u-boot中的weak
关键字:__weak
用例:
int __weak fpga_loadbitstream(int devnum, char *fpgadata, size_t size,
bitstream_type bstype)
{
printf("Bitstream support not implemented for this FPGA devicen");
return FPGA_FAIL;
}
linux应用层中
关键字:_attribute__((weak))
用例:
int __attribute__((weak)) func(......)
{
return 0;
}
作用
从单词词义理解,就是弱的意思,用来修饰函数,就是弱函数的意思。
weak、弱符号
若两个或两个以上全局符号(函数或变量名)名字一样,而其中之一声明为weak属性,则这些全局符号不会引发重定义错误。链接器会忽略弱符号,去使用普通的全局符号来解析所有对这些符号的引用,但当普通的全局符号不可用时,链接器会使用弱符号。
当有函数或变量名可能被用户覆盖时,该函数或变量名可以声明为一个弱符号。
GCC官方的描述:http://gcc.gnu.org/onlinedocs...
-
c语言weak弱化符号
2021-11-22 18:04:07char *TCID = "test weak"; 文件b.c内容: #include <stdio.h> char *TCID __attribute__((weak)) = "aaaaaaaaa"; int main() { printf("TCID is: %s\n", TCID); return 0; } 测试 执行gcc a.c b.c 输出... -
【嵌入式】C语言高级编程-强符号和弱符号(09)
2020-07-09 16:06:34weak属性02. 变量强符号和弱符号03. 函数强符号和弱符号04. 弱符号的作用05. alias属性06. 附录 01. weak属性 GNU C 通过 attribute 声明weak属性,可以将一个强符号转换为弱符号。 用法: void __attribute__((weak...00. 目录
01. weak属性
GNU C 通过 attribute 声明weak属性,可以将一个强符号转换为弱符号。
用法:
void __attribute__((weak)) func(void); int num __attribte__((weak);
编译器在编译源程序时,无论你是变量名、函数名,在它眼里,都是一个符号而已,用来表征一个地址。编译器会将这些符号集中,存放到一个叫符号表的 section 中。
在一个软件工程项目中,可能有多个源文件,由不同工程师开发。有时候可能会遇到这种情况:A 工程师在他负责的 A.c 源文件中定义了一个全局变量 num,而 B 工程师也在他负责的 B.c 源文件中定义了一个同名全局变量 num。那么当我们在程序中打印变量 num 的值时,是该打印哪个值呢?
是时候表演真正的技术了。这时候,就需要用编译链接的原理知识来分析这个问题了。编译链接的基本过程其实很简单,主要分为三个阶段。
编译阶段:编译器以源文件为单位,将每一个源文件编译为一个 .o 后缀的目标文件。每一个目标文件由代码段、数据段、符号表等组成。
链接阶段:链接器将各个目标文件组装成一个大目标文件。链接器将各个目标文件中的代码段组装在一起,组成一个大的代码段;各个数据段组装在一起,组成一个大的数据段;各个符号表也会集中在一起,组成一个大的符号表。最后再将合并后的代码段、数据段、符号表等组合成一个大的目标文件。
重定位:因为各个目标文件重新组装,各个目标文件中的变量、函数的地址都发生了变化,所以要重新修正这些函数、变量的地址,这个过程称为重定位。重定位结束后,就生成了可以在机器上运行的可执行程序。
上面举例的工程项目,在编译过程中的链接阶段,可能就会出现问题:A.c 和 B.c 文件中都定义了一个同名变量 num,那链接器到底该用哪一个呢?解决该问题就涉及强符号和弱符号问题。
02. 变量强符号和弱符号
在一个程序中,无论是变量名,还是函数名,在编译器的眼里,就是一个符号而已。符号可以分为强符号和弱符号。
- 强符号:函数名、初始化的全局变量名;
- 弱符号:未初始化的全局变量名。
在一个工程项目中,对于相同的全局变量名、函数名,我们一般可以归结为下面三种场景。
- 强符号+强符号
- 强符号+弱符号
- 弱符号+弱符号
强符号和弱符号在解决程序编译链接过程中,出现的多个同名变量、函数的冲突问题非常有用。一般我们遵循下面三个规则。
- 一山不容二虎
- 强弱可以共处
- 体积大者胜出
为了方便,这是我编的顺口溜。主要意思就是:在一个项目中,不能同时存在两个强符号,比如你在一个多文件的工程中定义两个同名的函数,或初始化的全局变量,那么链接器在链接时就会报重定义的错误。但一个工程中允许强符号和弱符号同时存在。比如你可以同时定义一个初始化的全局变量和一个未初始化的全局变量,这种写法在编译时是可以编译通过的。编译器对于这种同名符号冲突,在作符号决议时,一般会选用强符号,丢掉弱符号。还有一种情况就是,一个工程中,同名的符号都是弱符号,那编译器该选择哪个呢?谁的体积大,即谁在内存中存储空间大,就选谁。
程序示例
#include <stdio.h> int a = 1; int b; void fun(void) { printf("fun a = %d\n", a); printf("fun b = %d\n", b); } int a; int b = 2; int main(void) { printf("main a = %d\n", a); printf("main b = %d\n", b); fun(); return 0; }
执行结果
deng@itcast:~/tmp$ gcc test.c deng@itcast:~/tmp$ ./a.out main a = 1 main b = 2 fun a = 1 fun b = 2
我们在 main.c 和 func.c 中分别定义了两个同名全局变量 a 和 b,但是一个是强符号,一个是弱符号。链接器在链接过程中,看到冲突的同名符号,会选择强符号,所以你会看到,无论是 main 函数,还是 func 函数,打印的都是强符号的值。
一般来讲,不建议在一个工程中定义多个不同类型的弱符号,编译的时候可能会出现各种各样的问题,这里就不举例了。在一个工程中,也不能同时定义两个同名的强符号,即初始化的全局变量或函数,否则就会报重定义错误。但是我们可以使用 GNU C 扩展的 weak 属性,将一个强符号转换为弱符号。
程序示例
# test.c #include <stdio.h> int a = 4; void fun(void); int main(void) { printf("main a = %d\n", a); fun(); return 0; } # fun.c #include <stdio.h> int a __attribute__((weak)) = 1; void fun(void) { printf("fun a = %d\n", a); }
执行结果
deng@itcast:~/tmp$ gcc test.c fun.c deng@itcast:~/tmp$ ./a.out main a = 4 fun a = 4 deng@itcast:~/tmp$
通过 weak 属性声明,将 func.c 中的全局变量 a,转换为一个弱符号,然后在 main.c 里同样定义一个全局变量 a,并初始化 a 为4。链接器在链接时会选择 main.c 中的这个强符号,所以在两个文件中,打印变量 a 的值都是4。
03. 函数强符号和弱符号
链接器对于同名变量冲突的处理遵循上面的强弱规则,对于函数同名冲突,同样也遵循相同的规则。函数名本身就是一个强符号,在一个工程中定义两个同名的函数,编译时肯定会报重定义错误。但我们可以通过 weak 属性声明,将其中一个函数转换为弱符号。
程序示例
//fun.c #include <stdio.h> int a __attribute__((weak)) = 1; void __attribute__((weak)) fun(void) { printf("fun a = %d\n", a); } // test.c #include <stdio.h> int a = 4; void fun(void) { printf("test.c fun....\n"); } int main(void) { printf("main a = %d\n", a); fun(); return 0; }
执行结果
deng@itcast:~/tmp$ gcc fun.c test.c deng@itcast:~/tmp$ ./a.out main a = 4 test.c fun....
在这个程序示例中,我们在 main.c 中重新定义了一个同名的 func 函数,然后将 func.c 文件中的 func() 函数,通过 weak 属性声明转换为一个弱符号。链接器在链接时会选择 main.c 中的强符号,所以我们在 main 函数中调用 func() 时,实际上调用的是 main.c 文件里的 func() 函数。
04. 弱符号的作用
在一个源文件中引用一个变量或函数,当我们只声明,而没有定义时,一般编译是可以通过的。这是因为编译是以文件为单位的,编译器会将一个个源文件首先编译为 .o 目标文件。编译器只要能看到函数或变量的声明,会认为这个变量或函数的定义可能会在其它的文件中,所以不会报错。甚至如果你没有包含头文件,连个声明也没有,编译器也不会报错,顶多就是给你一个警告信息。但链接阶段是要报错的,链接器在各个目标文件、库中都找不到这个变量或函数的定义,一般就会报未定义错误。
当函数被声明为一个弱符号时,会有一个奇特的地方:当链接器找不到这个函数的定义时,也不会报错。编译器会将这个函数名,即弱符号,设置为0或一个特殊的值。只有当程序运行时,调用到这个函数,跳转到0地址或一个特殊的地址才会报错。
程序示例
#include <stdio.h> int a = 4; void __attribute__((weak)) fun(void); int main(void) { printf("main a = %d\n", a); fun(); return 0; }
执行结果
deng@itcast:~/tmp$ gcc test.c deng@itcast:~/tmp$ ./a.out main a = 4 段错误 (核心已转储)
在这个示例程序中,我们没有定义 func() 函数,仅仅是在 main.c 里作了一个声明,并将其声明为一个弱符号。编译这个工程,你会发现是可以编译通过的,只是到了程序运行时才会出错。
为了防止函数运行出错,我们可以在运行这个函数之前,先做一个判断,即看这个函数名的地址是不是0,然后再决定是否调用、运行。这样就可以避免段错误了,示例代码如下。
程序示例
#include <stdio.h> int a = 4; void __attribute__((weak)) fun(void); int main(void) { printf("main a = %d\n", a); if (NULL != fun) { fun(); } return 0; }
执行结果
deng@itcast:~/tmp$ gcc test.c deng@itcast:~/tmp$ ./a.out main a = 4
函数名的本质就是一个地址,在调用 func 之前,我们先判断其是否为0,为0的话就不调用了,直接跳过。你会发现,通过这样的设计,即使这个 func() 函数没有定义,我们整个工程也能正常的编译、链接和运行!
弱符号的这个特性,在库函数中应用很广泛。比如你在开发一个库,基础的功能已经实现,有些高级的功能还没实现,那你可以将这些函数通过 weak 属性声明,转换为一个弱符号。通过这样设置,即使函数还没有定义,我们在应用程序中只要做一个非0的判断就可以了,并不影响我们程序的运行。等以后你发布新的库版本,实现了这些高级功能,应用程序也不需要任何修改,直接运行就可以调用这些高级功能。
弱符号还有一个好处,如果我们对库函数的实现不满意,我们可以自定义与库函数同名的函数,实现更好的功能。比如我们 C 标准库中定义的 gets() 函数,就存在漏洞,常常成为黑客堆栈溢出攻击的靶子。
#include <stdio.h> int main(void) { char a[10]; gets(a); puts(a); return 0; }
C 标准定义的库函数 gets() 主要用于输入字符串,它的一个 Bug 就是使用回车符来判断用户输入结束标志。这样的设计很容易造成堆栈溢出。比如上面的程序,我们定义一个长度为10的字符数组用来存储用户输入的字符串,当我们输入一个长度大于10的字符串时,就会发生内存错误。
接着我们定义一个跟 gets() 相同类型的同名函数,并在 main 函数中直接调用,代码如下。
#include <stdio.h> char *gets(char *str) { printf("hello 123456789\n"); return NULL; } int main(void) { char a[10]; gets(a); return 0; }
执行结果
deng@itcast:~/tmp$ gcc test.c deng@itcast:~/tmp$ ./a.out hello 123456789 deng@itcast:~/tmp$
通过运行结果,我们可以看到,虽然我们定义了跟 C 标准库函数同名的 gets() 函数,但编译是可以通过的。程序运行时调用 gets() 函数时,就会跳转到我们自定义的 gets() 函数中运行。
05. alias属性
GNU C 扩展了一个 alias 属性,这个属性很简单,主要用来给函数定义一个别名。
程序示例
#include <stdio.h> void _fun(void) { printf("_fun\n"); } void fun() __attribute__((alias("_fun"))); int main(void) { char a[10]; fun(); return 0; }
执行结果
deng@itcast:~/tmp$ gcc test.c deng@itcast:~/tmp$ ./a.out _fun
通过 alias 属性声明,我们就可以给 _fun() 函数定义一个别名 fun(),以后我们想调用 _fun() 函数,可以直接通过 fun() 调用即可。
在 Linux 内核中,你会发现 alias 有时会和 weak 属性一起使用。比如有些函数随着内核版本升级,函数接口发生了变化,我们可以通过 alias 属性给这个旧接口名字做下封装,起一个新接口的名字。
//f.c void __f(void) { printf("__f()\n"); } void f() __attribute__((weak,alias("__f"))); //main.c void __attribute__((weak)) f(void); void f(void) { printf("f()\n"); } int main(void) { f(); return 0; }
当我们在 main.c 中新定义了 f() 函数时,在 main 函数中调用 f() 函数,会直接调用 main.c 中新定义的函数;当 f() 函数没有新定义时,就会调用 __f() 函数。
06. 附录
参考:C语言嵌入式Linux高级编程
-
转载嵌入式C语言中__weak的含义
2022-06-24 23:55:41嵌入式C语言中__weak的含义 -
weak实现原理
2021-12-07 19:48:40weak 实现原理 下面的一段代码是我们在开发中常见的weak的使用 - (void)viewDidLoad { [super viewDidLoad]; NSObject *obj = [[NSObject alloc] init]; __weak NSObject *weakp = obj; } 汇编 runtime调用objc... -
Objective-C语言特性——强引用、弱引用(Strong、Weak)
2014-11-28 09:17:46转自:独自一人 » Objective-C语言特性——强引用、弱引用(Strong、Weak) 编写Objective-C代码的很大一部分工作是管理可执行代码保持的对象引用,还有被引用对象保持的对另外一个对象的引用。在ARC出现之前,... -
Weak函数宏定义声明
2019-08-15 11:33:32Weak函数宏定义声明 //WEAK_FUN(int, func_name, int a, int b, int c); #define WEAK_FUN(type,name,args...) \ type __##name(args){return 0;}\ type name(args)__attribute__((weak, alias("__"#name))) .... -
浅谈Objective-C中的weak那些事
2021-05-19 16:32:59在objective-c中,weak几乎无处不在。尤其是定义ivar时,经常要用到这个关键字。用weak修饰的变量,在对象释放之后,对象会自动置为nil。它是怎么做到的呢?在研究它之前,有几个词有必要先了解一下。关键词... -
C语言之强化,弱化符号weak
2015-09-11 09:29:53C语言之强化,弱化符号weak 转自:http://blog.csdn.net/astrotycoon/article/details/8008629 一、概述 在C语言中,函数和初始化的全局变量(包括显示初始化为0)是强符号,未初始化的全局变量是弱符号。 对于... -
华为技术有限公司c语言编程规范.pdf.zip_C语言规范_pdf_weak9pr_华为 代码规范_华为c语言规范
2022-07-15 06:55:29华为的代码规范,C语言版,适合嵌入式行业的工程师 -
__weak 关键字的作用
2018-08-13 13:47:20在使用STM32的hal库的时候,我们常常可以看到很多库自带的函数有很多是使用__weak修饰的,比如: 这个修饰符的作用是什么呢?先看看单词的意思: 我们可以知道这是弱的意思,具体弱在哪里?又是和什么比较? ... -
ARM 之十一__weak 和 __attribute__((weak)) 关键字的使用
2020-06-17 19:53:19之前对于 __weak 关键字一直是一个简单的认知:编译器自动使用没有 __weak 的同名函数(如果有的话)替换有 __weak 关键字的同名函数,__weak 函数可以没有定义,且编译器不会报错! 至于这个参数详细的使用细节一直... -
C语言 : weak_alias描述
2018-07-08 11:53:08weak-alias 是一个宏,其目的是为函数添加一个”弱”别名,与”强”符号函数名区分。 说明, 如果调用函数无对应的函数无”强”符号对应的函数,则会调用该别名对应的函数 C/C++ 函数调用是以编译后的”符号”做... -
浅尝辄止5-C语言-weak symbol
2021-05-21 12:39:09如果你是写上层代码的程序员(名叫上上),需要下层程序员(名叫下下)提供一个新的API,为了保证版本库里的...weak symbol弱符号,这涉及到编译中符号的概念。在Linux开发环境中,有强符号和弱符号,符号简单来说就是... -
新手小心:c语言中强符号与弱符号的使用
2020-12-25 23:07:04c语言的强符号和弱符号是c初学者经常容易犯错的地方。而且很多时候,特别是多人配合开发的程序,它引起的问题往往非常行为怪异而且难以定位。什么是强符号和弱符号?在c语言中,函数和初始化的全局变量是强符号,未... -
C语言--__attribute__((weak))
2019-01-11 10:26:281、在其他文件中定义要调用的函数 main.c文件 #include<...char __attribute__((weak))Fun_Sum(char a, char b) { return a + b; } int main(int argc, char *argv[]) { char sum1 = 0; ... -
C++中的virtual虚函数是什么?它和普通函数有什么不同?为什么要用虚函数?
2021-05-21 12:00:04事实上,每一个C++程序员都不可能避开虚函数的,就像每一个C语言程序员都不可能避开指针一样。 再总结下“什么是虚函数” 理论是少不了的,它可以加深和尽可能全面的帮助我们理解概念。基类中的虚函数允许派生类... -
c语言之函数或者变量的weak属性 C语言之强化,弱化符号weak
2018-10-27 22:25:40#define __weak __attribute__((weak)) 如果这个关键字用在函数定义上面,一般情况下和一般函数没有两样。但是当有一个同名函数但是不带__weak被定义时,所有对这个函数的调用都是指向后者(不带__wea... -
C++及C语言 头文件函数(第一弹)
2019-08-31 13:42:57C++及C语言 头文件函数(第一弹) —字符判断 特别说明:以下"判断"皆为是则为非0值,不是则为0 1.bool isalpha (char x) 判断一个字符是否为字母(a−z,A−Z)(a-z,A-Z)(a−z,A−Z) 手动代码如下: bool isalpha... -
Keil-MDK 中 [WEAK] 的作用
2014-03-20 16:13:38时, 顺带介绍了 [WEAK] 的作用. 昨天再思考移植问题的时候(也就是执行第一个任务时直接跳到 SVC_Handler 里的 B . 处), 想到了这个问题, 然后在移植配置文件中添加了几个宏定义就解决了问题, 移植成功, 具体下文... -
Weak关键字介绍
2017-05-12 10:18:19由浅入深先来看看最简单的一个例子:#import"ViewController.h"@interfaceViewController()@property(nonatomic,strong)idstrongPoint;@property(nonatomic,weak)idweakPoint;@end@implementationViewControll... -
浅谈C语言中的强符号、弱符号、强引用和弱引用
2021-05-19 16:32:56这样做时要分清定义和声明的区别,__attribute__((weak)) int i 是定义变量并转换为弱符号,这样i是分配了空间的,而__attribute__((weak)) extern int i 则将原来定义的变量i由强符号转换为弱符号,导致使用i时不是... -
TIPS9_C语言__weak属性
2019-09-19 14:22:26C语言的__weak int timer_init(void)中__attribute__((weak)) 当一个工程足够大时,我们并不确认外部模块是否提供了一个我们想要的函数func,但是我们自己的模块又必须用到这个func函数,于是导致: 1.外部模块存在...