关于C语言中弱符号与弱引用的实际应用问题

最近在学习《程序员的自我修养——链接、装载与库》时,get到了一个新的知识点:弱符号与弱引用。书中简短的介绍,让我了解到弱符号的含义以及使用方式。了解我的朋友,应该知道我喜欢将知识点与我们实际工作结合起来,在工作中利用起来,正所谓学以善用。根据我的理解,觉得利用弱符号的特性可以帮组我们在工作中编写出更加稳定,可复用,可组合的优秀代码。在此向大家分享。

符号重定义错误

在编码过程中,我们经常遇到符号重定义的错误。编译器会报如下错误:

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语言中弱符号与弱引用的实际应用的文章就介绍到这了,更多相关C语言弱符号与弱引用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈C语言中的强符号、弱符号、强引用和弱引用

    首先我表示很悲剧,在看<程序员的自我修养--链接.装载与库>之前我竟不知道C有强符号.弱符号.强引用和弱引用.在看到3.5.5节弱符号和强符号时,我感觉有些困惑,所以写下此篇,希望能和同样感觉的朋友交流也希望高人指点. 首先我们看一下书中关于它们的定义. 引入场景:(1)文件A中定义并初始化变量i(int i = 1), 文件B中定义并初始化变量i(int i = 2).编译链接A.B时会报错b.o:(.data+0x0): multiple definition of `i':a.o:(.d

  • C语言中的强符号和弱符号介绍

    之前在extern "C" 用法详解中已经提到过符号的概念,它是编译器对变量和函数的一种标记,编译器对C和C++代码在生产符号时规则也是不一样的,符号除了本身名字的区别外,还有强符号和弱符号之分 我们先看一段简单的代码 复制代码 代码如下: /* test.c */  void hello();  int main()  {      hello();      return 0;  } 很显然,这段代码是没法链接通过的,它会报错undefined reference to hello

  • C 语言的弱符号与弱引用你了解吗

    目录 C语言中的__attribute__((weak)) 与 attribute ((weakref()) 弱符号 弱符号的作用与示例 弱引用 测试代码1: 测试代码2: 总结 C语言中的__attribute__((weak)) 与 attribute ((weakref()) 引言:最近在看 linux 中一些驱动代码.驱动代码中为了实现程序的扩展性和兼容性用了很多 C 语言中的高级特性.本节就来谈一谈 C 语言中的弱符号和弱引用的用法. 弱符号 弱符号是指在定义或者声明一个对象(变量.结

  • 新手小心:c语言中强符号与弱符号的使用

    声明:下面的实例全部在linux下尝试,window下未尝试.有兴趣者可以试一下.文章针c初学者.c语言的强符号和弱符号是c初学者经常容易犯错的地方.而且很多时候,特别是多人配合开发的程序,它引起的问题往往非常行为怪异而且难以定位.什么是强符号和弱符号?在c语言中,函数和初始化的全局变量是强符号,未初始化的全局变量时弱符号.强符号和弱符号的定义是连接器用来处理多重定义符号的,它的规则是:不允许多个强符号:如果一个强符号和一个弱符号,这选择强符号:如果多个弱符号,则任意选一个.它的陷阱:上代码:

  • C语言中弱符号与弱引用的实际应用

    最近在学习<程序员的自我修养--链接.装载与库>时,get到了一个新的知识点:弱符号与弱引用.书中简短的介绍,让我了解到弱符号的含义以及使用方式.了解我的朋友,应该知道我喜欢将知识点与我们实际工作结合起来,在工作中利用起来,正所谓学以善用.根据我的理解,觉得利用弱符号的特性可以帮组我们在工作中编写出更加稳定,可复用,可组合的优秀代码.在此向大家分享. 符号重定义错误 在编码过程中,我们经常遇到符号重定义的错误.编译器会报如下错误: multiple definition of `xxx'; 这

  • 关于C语言中弱符号与弱引用的实际应用问题

    最近在学习<程序员的自我修养--链接.装载与库>时,get到了一个新的知识点:弱符号与弱引用.书中简短的介绍,让我了解到弱符号的含义以及使用方式.了解我的朋友,应该知道我喜欢将知识点与我们实际工作结合起来,在工作中利用起来,正所谓学以善用.根据我的理解,觉得利用弱符号的特性可以帮组我们在工作中编写出更加稳定,可复用,可组合的优秀代码.在此向大家分享. 符号重定义错误 在编码过程中,我们经常遇到符号重定义的错误.编译器会报如下错误: multiple definition of `xxx'; 这

  • jsp页面中表达式语言中的$符号不起作用的解决方法

    今天myeclipse里部署了之前做的一个测试项目,发现jsp里的$符号tomcat启动后页面上显示出来了,百度搜了下别人也有类似的问题出现过.经提醒原来是web.xml配置的version设置的是2.5而我tomcat5启动的.是tomcat的版本低于web的版本,从而导致$符号不能正常使用. 后将tomcat5改用tomcat6.jdk采用1.6 启动spring2.5项目.$显示问题解决. 以下是网上摘录的详细说明: 在jsp页面中用表达式语言中的$符号,如${pageScope.titl

  • C语言中无符号与有符号及相加问题

    C语言中无符号与有符号问题 unsigned char a[5] = { 12,36,96,128,182 }; a[]范围为0~256. 数组中数都有效. char a[5] = { 12,36,96,128,182 }; a[]范围为-128~127. 数组中128和182均无效. C语言中无符号数和有符号数相加问题 看个题: #include<stdio.h> int main() { unsigned int a=6; int b=-20; printf("%d\n"

  • C语言中的链接编写教程

    链接   链接就是将不同部分的代码和数据收集和组合成为一个单一文件的过程,这个文件可被加载或拷贝到存储器执行.   链接可以执行与编译时(源代码被翻译成机器代码时),也可以执行与加载时(在程序被加载器加载到存储器并执行时),甚至执行与运行时,由应用程序来执行.在现代系统中,链接是由链接器自动执行的.   链接器分为:静态链接器和动态链接器两种. 静态链接器   静态链接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出. 静态链接器主要完成两个

  • PHP弱类型语言中类型判断操作实例详解

    本文实例讲述了PHP弱类型语言中类型判断操作.分享给大家供大家参考,具体如下: 1.php一个数字和一个字符串进行比较或者进行运算时,PHP会把字符串转换成数字再进行比较.PHP转换的规则的是:若字符串以数字开头,则取开头数字作为转换结果,若无则输出0. 例如:123abc转换后应该是123,而abc则为0,0==0这当然是成立的.具体可以参考官方手册:如果比较一个整数和字符串,则字符串会被转换为整数 $a = ($b=4)+5; echo $a; //9 echo '<p>'; var_du

随机推荐