1. 强符号和弱符号

1.1 u-bootkernel中的__weak指令

u-bootkernel比较普遍地使用了__weak来定义函数。

include\linux\compiler-gcc.h__weak是这样定义的:

#define __weak              __attribute__((weak))
  • 1

GCC通过__attribute__((weak))指令定义的函数或变量称为弱符号(Weak Symbol),实际上这个指令大部分时候都是用来定义函数,很少用于定义变量。

编译器默认函数和初始化了的全局变量为强符号(Strong Symbol),未初始化的全局变量为弱符号(Weak Symbol)

1.2 强符号和弱符号的链接规则

<<程序员的自我修养>>第三章对这个强弱符号的链接规则做了很好的总结:

针对强弱符号的概念,链接器就会按照如下规则处理与选择被多次定义的全局符号:

  • 规则1:不允许强符号被多次定义(即不同的目标文件中不能有同名的强符号);如果有多个强符号定义,则链接器报符号重复定义错误。
  • 规则2:如果一个符号在某个目标文件中是强符号,在其他文件中都是弱符号,那么选择强符号。
  • 规则3:如果一个符号在所有目标文件中都是弱符号,那么选择其中占用空间最大的一个。比如目标文件A定义全局变量global为int型,占4个字节;目标文件B定义global为doulbe型,占8个字节,那么目标文件A和B链接后,符号global占8个字节(尽量不要使用多个不同类型的弱符号,否则容易导致很难发现的程序错误)。
  • 规则1很好理解,如果程序中多次定义了一个初始化过的全局变量(如int a=0),则编译器会报告multiple definition的错误,比较常见。

多次定义的全局函数也属于此类,因为函数名也是一个符号变量,只不过它指向了随后定义的函数体,而且这个变量是只读的,不可改变。这也是为什么函数名可以赋值给一个变量,但是却无法给函数名赋值的原因。

  • 规则2就是u-bootkernel中最常用的技巧了。

首先在平台无关的代码中定义一个__weak属性的函数,如u-bootboard_f.c的文件头部定义了很多led相关的空函数:

__weak void coloured_LED_init(void) {}
__weak void red_led_on(void) {}
__weak void red_led_off(void) {}
__weak void green_led_on(void) {}
__weak void green_led_off(void) {}
__weak void yellow_led_on(void) {}
__weak void yellow_led_off(void) {}
__weak void blue_led_on(void) {}
__weak void blue_led_off(void) {}

如果需要这些led相关的功能,再在相关的文件中重新定义并实现这些函数即可,例如文件gpio_led.c中在宏CONFIG_GPIO_LED_STUBS打开时就调用__led_set重新实现了这些函数。

  • 规则3指明了同名弱符号的多个定义以占用空间最大的为准,主要是针对不用类型的变量而言;对于函数,其对应的符号相当于一个只读指针变量,而指针类型大小是固定的,不存在这个问题。

2. 强引用和弱引用

<<程序员的自我修养>>第三章对也对强引用和弱引用做了说明:

弱引用和强引用 目前我们所看到的对外部目标文件的符号引用在目标文件被最终链接成可执行文件时,它们必须要被正确决议,如果没有找到该符号的定义,链接器就会报符号未定义错误,这种被称为强应用(Strong Reference)。与之相对应还有一种弱引用(Weak Reference),在处理弱引用时,如果该符号有定义,则链接器将该符号的引用决议;如果该符号未被定义,则链接器对于该引用不报错。链接器处理强引用和弱引用的过程几乎一样,只是对于未定义的弱引用,链接器不认为它是一个错误。一般对于未定义的弱引用,链接器默认其为0,或者是一个特殊的值,以便于程序代码能够识别。

GCC中,我们可以通过使用__attribute__((weakref))扩展关键字来声明对一个外部函数的引用为弱引用。比如下面这段代码:

__attribute__ ((weakref)) void foo();
int main()
{
foo();
}

我们可以将它编译成一个可执行文件,GCC并不会报链接错误。但是当我们运行这个可执行文件时,会发生运行错误。因为当main函数试图条用foo函数是,foo函数的地址为0,于是发生了非法地址访问的错误。一个改进的例子是:

__attribute__ ((weakref)) void foo();
int main()
{
if (foo) foo();
}

这种弱符号弱引用对于库来说十分有用,比如库中定义的弱符号可以被用户定义的强符号所覆盖,从而使得程序可以使用自定义版本的库函数;或者程序可以对某些扩展功能模块的引用定义为弱引用,当我们将扩展模块与程序连接在一起时,功能模块就可以正常使用;如果我们去掉了某些功能模块,那么程序也可以正常链接,只是缺少了相应的功能,这使得程序的功能更加容易裁剪和组合。

专门检查过u-boot v2016.09linux v3.3-3.8的代码,没有找到采用弱引用来定义函数的情况,可见也不是很常用的做法。

4. 其它

对于弱符号(Weak Symbol)弱引用,其都仅是GNU工具链GCCC语言语法的扩展,并不是C本身的语言特性。 
有提到微软的MSVC就不支持弱符号和弱引用特性。

版权声明:本文为guyongqiangx原创,欢迎评论留言和转载收藏,转载请注明出处:http://blog.csdn.net/guyongqiangx

GCC中的强符号和弱符号及强引用和弱引用的更多相关文章

  1. GNU C/C++ __attributes__ GCC中的弱符号与强符号

    最近在看一些源代码,遇到了一些使用__attribute__修饰函数和变量的属性方面的代码,不是太了解,很是汗颜,再此做个总结:   GCC使用__attribute__关键字来描述函数,变量和数据类 ...

  2. GCC中的弱符号与强符号

    GCC中的弱符号与强符号 我们经常在编程中碰到一种情况叫符号重复定义.多个目标文件中含有相同名字全局符号的定义,那么这些目标文件链接的时候将会出现符号重复定义的错误.比如我们在目标文件A和目标文件B都 ...

  3. gcc/g++中weak弱符号及alias别名

    最近查看linux内核代码时,表现了一些编译器选项如__attribute_((weak)).__attribute__( (alias("target"))),一开始不了解,后来 ...

  4. C语言中的强符号与弱符号

    转自:http://blog.csdn.net/astrotycoon/article/details/8008629 一.概述 在C语言中,函数和初始化的全局变量(包括显示初始化为0)是强符号,未初 ...

  5. 关于C语言中的强符号、弱符号、强引用和弱引用的一些陋见,欢迎指正

    首先我表示很悲剧,在看<程序员的自我修养--链接.装载与库>之前我竟不知道C有强符号.弱符号.强引用和弱引用.在看到3.5.5节弱符号和强符号时,我感觉有些困惑,所以写下此篇,希望能和同样 ...

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

    摘自http://www.jb51.net/article/56924.htm 浅谈C语言中的强符号.弱符号.强引用和弱引用 投稿:hebedich 字体:[增加 减小] 类型:转载 时间:2014- ...

  7. 嵌入式C语言自我修养 09:链接过程中的强符号和弱符号

    9.1 属性声明:weak GNU C 通过 __atttribute__ 声明weak属性,可以将一个强符号转换为弱符号. 使用方法如下. void __attribute__((weak)) fu ...

  8. 浅谈C语言中的强符号、弱符号、强引用和弱引用【转】

    转自:http://www.jb51.net/article/56924.htm 首先我表示很悲剧,在看<程序员的自我修养--链接.装载与库>之前我竟不知道C有强符号.弱符号.强引用和弱引 ...

  9. C++中弱符号(弱引用)的意义及实例

    今天读别人代码时看到一个“#pragma weak”,一时没明白,上网研究了一个下午终于稍微了解了一点C.C++中的“弱符号”,下面是我的理解,不正确的地方望大家指正. 本文主要从下面三个方面讲“弱符 ...

随机推荐

  1. Jquery数字转盘:

    项目中,在充值流程中,加入了1个抽奖环节,需要转盘显示抽中的虚拟货币.网上找了相关的特效,最后锁定在这个特效上:http://www.jb51.net/jiaoben/319636.html.因为用的 ...

  2. Noip2011 Mayan游戏 搜索 + 模拟 + 剪枝

    写了一下午,终于AC了. 由于n<=5, 所以不需要太多的剪枝和技巧也能过.可以将操作后的消方块和下落和剪枝函数写到一个结构体中,这样会减少调试难度,更加简洁. 可以采用如下剪枝: 1. 如果当 ...

  3. K3 新单到老单关联字段的添加

    新单到老单字段的添加分为两种: 一种为文本字段信息的关联,新单与老单字段的信息均为文本字段: 另一种为基础资料信息的关联,新单与老单均为基础资料字段信息.       K3 WISE 11.0中存储老 ...

  4. Linux磁盘分区--GPT分区

    MBR分区表有一定的局限性,最大支持2.1tb硬盘,单块硬盘最多4个主分区. 这里就要引入GPT分区表,可以支持最大18EB的卷,最多支持128个主分区,所以如果使用大于2tb的卷,就必须使用GTP分 ...

  5. React 手稿 - Component state

    Component state 实例: import React, { PureComponent } from 'react'; export default class extends PureC ...

  6. JSP中文乱码问题的由来以及解决方法

    首先明确一点,在计算机中,只有二进制的数据! 一.java_web乱码问题的由来 1.字符集 1.1 ASCII字符集 在早期的计算机系统中,使用的字符非常少,这些字符包括26个英文字母.数字符号和一 ...

  7. socketserver模块初识

    python提供了两个级别访问的网络服务: 低级的网络服务支持基本的socket,它提供了标准的BSD sockets API,可以访问底层操作系统socket接口的全部方法 高级别的网络服务模块so ...

  8. ongl表达式中得到对象,调用对象方法(OA项目权限显示模块)

    在用户是否拥有某项权限的问题  是这样解决的: 用户登录之后  登录信息是保存在session域中的  通过el表达式可得到登录的对象信息  那么怎样判断用户是否拥有某项权限呢 ?如果没有上图中的判断 ...

  9. SSH框架整合截图总结(一)

    分页相关属性 --------------------------------------------------------------- 分页思路表单提交(只需传递当前页的值) ->acti ...

  10. oracle 有个xe版本

    oracle 有个xe版本 学习了:http://blog.csdn.net/angiexia/article/details/8615771