您好,登录后才能下订单哦!
这篇文章将为大家详细讲解有关C语言中弱符号与弱引用怎么用,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
在编码过程中,我们经常遇到符号重定义的错误。编译器会报如下错误:
multiple definition of `xxx';
这就是符号重复定义导致的,再往细里面说,是在同一作用域内符号冲突。我们知道变量是由作用域和生命周期概念的。比如:
例1: main.c int strong=0; int main() { printf("strong = %d\n",strong); return 0; } strong.c int strong=1;
gcc main.c strong.c -o main
则会报重定义错误。因为在main.c 和strong.c 文件中,整型变量strong是全局变量,它们的作用域都是跨文件的。若是在不同的作用域,即使相同变量名,也不会报错。编译器会有默认的优先级处理:总是更小作用域的变量覆盖更大作用域的变量,前提是这两个变量的作用域是包含或被包含的关系。比如:
例2: main.c int strong=0; int main() { printf("strong = %d\n",strong); return 0; } strong.c static int strong=1;
gcc main.c strong.c -o main
不再报错。此时main.c 中的strong 变量的作用域是跨文件,而strong.c中的strong变量的作用域仅限strong.c文件。因此不存在相同作用域中,符号重定义问题。并且结果输出为0;
同理,下面的代码会编译报错吗?输出为多少呢?
main.c int strong=0; int main() { int strong=2; printf("strong = %d\n",strong); return 0; } strong.c static int strong=1;
甚至于下面的代码也是合法的:
main.c int strong=0; int main() { int strong=2; if(1) { int strong=3; } { int strong = 4; printf("strong = %d\n",strong); } return 0; }
在C语言中,我们可以简单地认为花括号是文件内作用域的分隔符。
编译器默认函数和已初始化的全局变量为强符号,而未初始化的全局变量为弱符号;同时开发者可以通过"attribute((weak))"来声明一个符号为弱符号;
gcc 在编译过程中,对于强弱符号遵循一定规则进行取舍:
当有多个为强符号时,报"redefinition of ‘xxx'"错误
当仅有一个为强符号时,选取强符号的值
当都为弱符号时,选择其中暂用空间较大的符号。(防止溢出越界等问题)—— 这个应该和编译器有关,我在本地环境中,是不允许多个类型不同的弱符号存在的。编译会出错。
很明显,例1 则是出现多个强符号,导致的redefinition 错误。例如下面code:
main.c int strong; int strong=2; int main() { printf("strong = %d\n",strong); return 0; }
编译并不会出错,并且输出为2;
我们知道在编译成可执行文件时,若源文件引用了外部目标文件的符号,在链接过程中,需要找到对应的符号定义,若未找到对应符号(未定义),链接器会报符号位未定义错误,导致编译出错。这种被称为强引用。与相对应的时弱引用(开发者可通过attribute((weakref))声明),链接器在链接符号过程中,若发现符号为弱引用,即使没有找到符号定义,链接时也不会报错,但是会将该引用默认为0;
书中的代码如下:
main.c int strong; int strong=2; int main() { printf("strong = %d\n",strong); return 0; }
虽然没有定义foo(),但是我们可以将它编译成可执行文件,并且GCC 编译不会报链接错误,但是当我们运行时,会发生运行错误。实际上新版本的编译器上诉的代码会在链接时报错的,新版本的示例代码应该如下:(新版本的weakref 需要函数别名,且必须是static 修饰)
main.c static __attribute__((weakref("foo"))) void myfoo(void); void main(void) { if(myfoo) { myfoo(); } }
新版的弱符号引用如上所示。即表示若没有找到foo函数,编译不报错,但是会默认myfoo为NULL。若在其他库中定义了foo函数,对myfoo的引用就相当于对foo的引用。这种弱引用在库的使用上十分有用的。
经过上面的描述,我们了解到了强符号,弱符号,强引用,弱引用的概念。我认为起码有两点特性可以在我们工作中使用:
强符号可以替换弱符号。
弱引用可以避免函数未定义的错误。
一些库中对外接口可以声明为弱符号。比如:
在math库中,我们发现add(int num1, int num2)
这个接口存在问题,那我们解决方式一般有以下几种:
1. 实现一个myadd(int num1,int num2)
接口,之后再将项目中的所有add
,替换为myadd
。这种方式可行,但是存在缺点:修改量大,并且后续人员不清楚背景,很有可能继续使用熟悉的add
接口。
2. 更新math库,从更本解决此问题。这种方式比较推荐。但是也并不是通用的,比如有些库并不是开源的,并且已经过了支持日期,也就不适用了。
此时,我们可以自己在项目中定义一个add(int num1,int num2)
接口,用强符号替换库中的弱符号,这样改动是比较小的。(这种情景需要了解接口的实现内容,可给调用者较高的重构权力)
应用层的开发,离不开sdk的提供,一般sdk维护了,即使应用没有需求发生,往往也会为了配合sdk,进行简单的修改。以设备升级作为举例,若升级过程中,分为传包(pass),验签(verify),解密(decode),安装(install),上传日志(report)等步骤,并且这些核心接口都是以libsdk.so库的形式提供给应用工程师。那么正常情况下,应用逻辑大致如下:
用户业务流程 ... pass(); ... verify(); ... decode(); ... install(); ... report(); ...
但是这样的业务代码,我觉得是非常差的。比如新的项目中,不需要做解密包操作了,(理论上libsdk.so库中应该不具备decode接口了),这样就会导致应用程序编译失败。undefine 'decode'
。
因此我建议应用代码可以如下:
static __attribute__((weakref("pass"))) void mypass(void); static __attribute__((weakref("verify"))) void myverify(void); static __attribute__((weakref("decode"))) void mydecode(void); static __attribute__((weakref("install"))) void myinstall(void); static __attribute__((weakref("report"))) void myreport(void); 用户业务流程 ... if(mypass) mypass(); else printf("don't need pass\n"); ... if(myverify) myverify(); else printf("don't need verify\n"); ... if(mydecode) mydecode(); else printf("don't need decode\n"); ... if(myinstall) myinstall(); else printf("don't need install\n"); ... if(myreport) myreport(); else printf("don't need report\n"); ...
关于“C语言中弱符号与弱引用怎么用”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。