浅谈C语言中的强符号、弱符号、强引用和弱引用【转】
转自:http://www.jb51.net/article/56924.htm
首先我表示很悲剧,在看《程序员的自我修养--链接、装载与库》之前我竟不知道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:(.data+0x0): multiple definition of `i'。(2)在文件C中定义并初始化两个变量i(int i = 1; int i = 2), 编译链接时会报错c.c:2:5: error: redefinition of ‘i'; c.c:1:5: note: previous definition of ‘i' was here。
强符号:像场景中这样的符号定义被称为强符号,对于C/C++来说,编译器默认函数和初始化的全局变量为强符号。
弱符号:接上文,为初始化的全局变量为弱符号。
编译器关于强弱符号的规则有:(1)强符号不允许多次定义,但强弱可以共存;(2)强弱共存时,强覆盖弱;(3)都是弱符号时,选择占用空间最大的,如选择 double类型的而不选择int类型的。
由以上定义所以有我之前没有想到的场景:
代码a.c:
1 int i = 2;
代码b.c:
int i;
int main(int argc, char** argv)
{
printf("i = %d\n", i);
return 0;
}
编译文件a和b并链接,结果输出i为2而不是0。
并且在同一个文件中定义但未初始化两个相同的变量不会报错,只有在使用变量时才会报错。
对于GCC编译器来说,还允许使用__attribute__((weak))来将强符号定义为弱符号,所已有
代码c.c
__attribute__((weak)) int i = 1;
int main(int argc, char** argv)
{
printf("i = %d\n", i);
return 0;
}
结果i的输出仍未2而不是1。
那么对于函数而言是不是也这样呢?先不看函数,而是先看由强弱符号而进一步引入的强弱引用。书中关于强弱引用的概述是对于强引用若未定义则链接时肯定会报错,而对于弱引用则不会报错,链接器默认其为0(这一点对于函数好理解,即函数符号所代表入口地址为0;对于变量就要注意了,既然是引用那自然就是地址了,所以同函数一样变量的地址为0而不是变量的值为0)。此时对于强弱引用是不是还没有什么明确的概念呢?到底什么是引用?引用和符号又是什么关系?这里我说一下我的理解(欢迎指正),在定义和声明处指定的函数名、变量名即为对应的符号,而在代码其他处调用函数或使用变量时,则把函说明和变量名看作引用,这样一来符号和引用在代码层面上其实就是一个东西,只是根据环境而叫法不同而已。那么强符号对应强引用,弱符号对应弱引用。
有上面的强弱引用的特点可看出,当一个函数为弱引用时,不管这个函数有没有定义,链接时都不会报错,而且我们可以根据判断函数名是否为0来决定是否执行这个函数。这样一来,包含这些函数的库就可以以模块、插件的形式和我们的引用组合一起,方便使用和卸载,并且由于强符号可以覆盖弱符号和强弱符号与强弱引用的关系可知,我们自己定义函数可以覆盖库中的函数,多么美妙。
先看根据条件判断是否执行函数:
代码d.c
void func()
{
printf("func()#1\n");
}
代码e.c
__attribute__((weak)) void func();
int main(int argc, char** argv)
{
if (func)
func();
return 0;
}
编译d.c,cc -c d.c 输出d.o;编译e.c并链接d.o,cc d.o e.c -o e输出可执行文件e,运行e正常执行函数func。编译e.c但不链接d.o,此时并不会报错,只不过func不会执行,因为没有它的定义所以if(func)为假。
再看函数覆盖:
代码f.c
__attribute__((weak)) void func()
{
printf("func()#1\n");
}
代码g.c
void func()
{
printf("func()#2\n");
}
int main(int argc, char** argv)
{
func();
return 0;
}
~
编译链接,结构输出"func()#2"。
以上可以说明函数和变量是保持一致的,其实对应变量也可以像使用函数那样先判断再使用,只不过不是判断变量的值而是变量的地址,如
代码v1.c
代码v2.c
__attribute__((weak)) extern int i;
int main(int argc, char** argv)
{
if (&i)
printf("i = %d\n", i);
return 0;
}
~
编译并链接v1时,输出2;编译但不链接v1时无输出。这样做时要分清定义和声明的区别,__attribute__((weak)) int
i 是定义变量并转换为弱符号,这样i是分配了空间的,而__attribute__((weak)) extern int i
则将原来定义的变量i由强符号转换为弱符号,导致使用i时不是强引用而是弱引用。不过虽然变量可以这么做但没有函数那样有意义。
上面关于强弱引用仍旧使用的是GCC提供的__attribute__((weak)),而书中还提到了__attribute__((weakref)),后者貌似更能体现“引用”这一关键词。而我之所以使用前者来介绍强弱引用,是因为我对关于强弱符号与强弱引用对应关系的理解。关于__attribute__((weakref))的使用方法,这里介绍一种(两者都有不同的使用方法)。
代码a.c
void bar()
{
printf("foo()\n");
}
代码b.c
static void foo() __attribute__((weakref("bar")));
int main(int argc, char** argv)
{
if (foo)
foo();
return 0;
}
注意函数foo的static修饰符,没有的话会报错,这样将函数foo限制在只有本文件内可使用。
浅谈C语言中的强符号、弱符号、强引用和弱引用【转】的更多相关文章
- 浅谈C语言中的强符号、弱符号、强引用和弱引用
摘自http://www.jb51.net/article/56924.htm 浅谈C语言中的强符号.弱符号.强引用和弱引用 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2014- ...
- 浅谈 C 语言中模块化设计的范式
今天继续谈模块化的问题.这个想慢慢写成个系列,但是不一定连续写.基本是想起来了,就整理点思路出来.主要还是为以后集中整理做点铺垫. 我们都知道,层次分明的代码最容易维护.你可以轻易的换掉某个层次上的某 ...
- 浅谈C语言中的联合体
联合体union 当多个数据须要共享内存或者多个数据每次仅仅取其一时.能够利用联合体(union).在C Programming Language 一书中对于联合体是这么描写叙述的: 1)联合体是一个 ...
- 浅谈c语言中的堆
操作系统堆管理器管理: 堆管理器是操作系统的一个模块,堆管理内存分配灵活,按需分配. 大块内存: 堆内存管理者总量很大的操作系统内存块,各进程可以按需申请使用,使用完释放. 程序手动申请&释放 ...
- 浅谈C#语言中的各种数据类型,与数据类型之间的转换
什么是数据类型? 数据类型,百度百科是这样解释的:数据类型在数据结构中的定义是一个值的集合以及定义在这个值集上的一组操作.这样的解释对于一个初学者来说未必太过于深奥. 简单点说,数据类型就是不同长度的 ...
- 浅谈C语言中结构体的初始化
转自:http://www.jb51.net/article/37246.htm <代码大全>建议在变量定义的时候进行初始化,但是很多人,特别是新人对结构体或者结构体数组定义是一般不会初始 ...
- 转: 浅谈C/C++中的指针和数组(二)
转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242419.html 浅谈C/C++中的指针和数组(二) 前面已经讨论了指针和数组 ...
- 转:浅谈C/C++中的指针和数组(一)
再次读的时候实践了一下代码,结果和原文不一致 error C2372: 'p' : redefinition; different types of indirection 不同类型的间接寻址 /// ...
- 浅谈关于QT中Webkit内核浏览器
关于QT中Webkit内核浏览器是本文要介绍的内容,主要是来学习QT中webkit中浏览器的使用.提起WebKit,大家自然而然地想到浏览器.作为浏览器内部的主要构件,WebKit的主要工作是渲染.给 ...
随机推荐
- 百度地图常用2.0使用以及调用js
/** * 生成一条路线 * @param {Object} baiduMap 百度地图的 map对象 * @param {Object} lineColor 线路颜色 * @param {Objec ...
- 【WCF】WCF 附录 高级主题 配置服务配额设置
微软产品自带一个“默认安全”方案.这也包括了WCF,意味着WCF中的多种配置可以设置来阻止诸如DOS(拒绝服务访问)攻击.微软为很多基于一个单一计算机的开发环境选择这样的设置.这也意味着默认设置中的一 ...
- intelliJ IDEA最常用的快捷键
一.使用相关快捷键 1.重写接口实现类:Ctrl+I 2.搜索:Shift+Shift 3.生成get或set方法快捷键:Alt+insert: 4.导入未实现的方法,强制类型转换:Alt+Ent ...
- [转]MATLAB cell数据类型
细胞型数据类型(cell)使不同类型和不同维数的数组可以共存,细胞型数组实际上可以认为是一种以任意形式的数组为分量的多维数组. 1.细胞型数据的定义 1)直接赋值定义:细胞型变量在定义时需要使用大括号 ...
- BZOJ4835 遗忘之树
点分树上的某个点和其某个子树在原树中的连接方式一般来说可以是由该点连向子树内任意一点,这样方案数即为所有子树大小之积.但有一种特殊情况是连接某点后导致编号最小的重心更换,只要去掉这种就行了,具体地可以 ...
- [ZJOJ2007]时态同步 贪心
不是很懂为什么luogu标签是树形DP,感觉我想的就是一个贪心啊... 随机造几组数据,我们发现贪心的确可以得到最优解,那么为什么呢? 假设将所有时态贪心的调整是对的,那么如果一个节点的各个儿子时态不 ...
- 【BZOJ5288】[HNOI2018]游戏(乱搞?)
[BZOJ5288][HNOI2018]游戏(乱搞?) 题面 BZOJ 洛谷 题面自己到洛谷上看把 题解 考场上乱搞拿到了\(90\)分,简直不敢相信. 回家把代码再交了一份直接就\(AC\)了??? ...
- 牛客网 Wannafly挑战赛27 蓝魔法师
蓝魔法师 链接: https://www.nowcoder.com/acm/contest/215/C 来源:牛客网 题目描述 "你,你认错人了.我真的,真的不是食人魔."--蓝魔 ...
- POJ. 2253 Frogger (Dijkstra )
POJ. 2253 Frogger (Dijkstra ) 题意分析 首先给出n个点的坐标,其中第一个点的坐标为青蛙1的坐标,第二个点的坐标为青蛙2的坐标.给出的n个点,两两双向互通,求出由1到2可行 ...
- React注释
React中注释有以下三种 var content = ( <Nav> {/* 一般注释, 用 {} 包围 */} <Person /* 多 行 注释 */ name={window ...