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

 更新时间:2015年03月17日 10:09:38   投稿:junjie  
这篇文章主要介绍了C语言中的强符号和弱符号介绍,本文用多个实例来讲解强符号和弱符号,需要的朋友可以参考下

之前在extern “C” 用法详解中已经提到过符号的概念,它是编译器对变量和函数的一种标记,编译器对C和C++代码在生产符号时规则也是不一样的,符号除了本身名字的区别外,还有强符号和弱符号之分

我们先看一段简单的代码

复制代码 代码如下:

/* test.c */ 
void hello(); 
int main() 

    hello(); 
    return 0; 


很显然,这段代码是没法链接通过的,它会报错undefined reference to hello,说的是hello未定义,因为这里我们只声明了函数hello,而没有定义它。但是我们把代码稍作修改如下
复制代码 代码如下:

__attribute__((weak)) void hello(); 
int main() 

    hello(); 
    return 0; 


这时你会发现,编译链接都可通过,但是运行会报错,因为这时我们将hello声明为了弱符号,在链接时弱符号会被链接器当做0,执行一个地址为0的函数当然会报错,改为如下代码就不会报错了,只是它没有任何输出
复制代码 代码如下:

__attribute__((weak)) void hello(); 
int main() 

    if(hello) 
        hello(); 
    return 0; 

编译器认为,函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号,链接器在处理强符号和弱符号时有如下规则

1.不同目标文件中,不允许有同名的强符号
2.如果一个符号在某个目标文件中是强符号,在其它目标文件中为弱符号,选择强符号
3.如果一个符号在所有目标文件中都是弱符号,选择占用空间最大的,比如目标文件A中有double global_var,文件B中有int global_var,double占用8字节,大于int的4字节,A和B链接后,符号global占8字节

对此我们可以简单的验证一下,有如下两个文件

复制代码 代码如下:

/* 1.c */ 
char global_var; 
int main() 

    return 0; 

 
/* 2.c */ 
int global_var; 

全局变量global_var在两个文件中都没有初始化,因此都是弱符号,执行编译命令gcc 1.c 2.c,用readelf查看符号表readelf -s a.out,为了查看方便我们只输出最后几行

复制代码 代码如下:

Num:    Value          Size Type    Bind   Vis      Ndx Name 
62: 0000000000600818     4 OBJECT  GLOBAL DEFAULT   25 global_var 
63: 0000000000400474    11 FUNC    GLOBAL DEFAULT   13 main 
64: 0000000000400358     0 FUNC    GLOBAL DEFAULT   11 _init 

这里符号global_var占用的size是4,说明链接器选择的是占用空间更大的int global_var,我们再稍作修改,将1.c中的全局变量初始化,如下

复制代码 代码如下:

/* 1.c */ 
char global_var = 1; 
int main() 

    return 0; 

 
/* 2.c */ 
int global_var; 

这时1.c中的global_var为强符号,2.c中的global_var为弱符号,同样编译之后用readelf查看符号表readelf -s a.out如下

复制代码 代码如下:

Num:    Value          Size Type    Bind   Vis      Ndx Name 
62: 0000000000600818     1 OBJECT  GLOBAL DEFAULT   25 global_var 
63: 0000000000400474    11 FUNC    GLOBAL DEFAULT   13 main 
64: 0000000000400358     0 FUNC    GLOBAL DEFAULT   11 _init 

此时符号global_var占用的size是1,说明链接器选择的是强符号

在写代码时应该尽量避免有不同类型的符号,否则会引发非常诡异且不易察觉的错误,为了避免可以采取如下措施:

1.上策:消除所有的全局变量
2.中策:将全局变量声明为static类型,并提供接口供访问
3.下策:全局变量一定要初始化,哪怕初始化为0
4.必备:打开gcc的-fno-common选项,它会禁止有不同类型的符号

说了这么多,好像在说应该尽量用强符号,那弱符号有什么用呢,所谓存在即合理,有时候我们甚至需要显示定义弱符号,这对库函数会非常有用,比如库中的弱符号可以被用户自定义的强符号覆盖,从而实现自定义的库版本,或者在使用某些扩展功能时,用户可以定义一个弱符号,当链接了该功能时,功能模块可以正常使用,如果去掉功能模块,程序也可正常链接,只是缺少某些功能而已,比如我们可以通过下面的代码判断程序是否链接了pthread库,从而决定执行什么样的操作

复制代码 代码如下:

/* test.c */ 
#include <stdio.h> 
#include <pthread.h> 
 
__attribute__((weak)) int pthread_create(  
    pthread_t*,  
    const pthread_attr_t*,  
    void*(*)(void*),  
    void*); 
 
int main() 

    if (pthread_create) 
    { 
        printf("This is multi-thread version!\n"); 
    } 
    else 
    { 
        printf("This is single-thread version!\n"); 
    } 
    return 0; 

编译运行结果如下

复制代码 代码如下:

$ gcc test.c 
$ ./a.out 
This is single-thread version! 
$ gcc test.c -lpthread 
$ a.out 
This is multi-thread version! 

相关文章

  • C++ 十进制转换为二进制的实例代码

    C++ 十进制转换为二进制的实例代码

    这篇文章介绍了C++ 十进制转换为二进制的实例代码,有需要的朋友可以参考一下
    2013-10-10
  • C++实现LeetCode(138.拷贝带有随机指针的链表)

    C++实现LeetCode(138.拷贝带有随机指针的链表)

    这篇文章主要介绍了C++实现LeetCode(138.拷贝带有随机指针的链表),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • 详谈全排列next_permutation() 函数的用法(推荐)

    详谈全排列next_permutation() 函数的用法(推荐)

    下面小编就为大家带来一篇详谈全排列next_permutation() 函数的用法(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-03-03
  • C语言实现简单版三子棋

    C语言实现简单版三子棋

    这篇文章主要为大家详细介绍了C语言实现简单版三子棋,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • C语言详细分析讲解关键字goto与void的作用

    C语言详细分析讲解关键字goto与void的作用

    我们在C语言中经常会见到void,也会偶尔见到goto,那么C语言中既然有goto,为什么我们在代码中见的很少呢?在以前很多的项目经验中,我们得到这样一条潜规则:一般项目都是禁用goto的,程序质量与goto的出现次数成反比。自后也就造成了我们一般不会使用goto
    2022-04-04
  • C++基于hook iat改变Messagebox实例

    C++基于hook iat改变Messagebox实例

    这篇文章主要介绍了C++基于hook iat改变Messagebox的方法,以实例形式展示了针对IAT(即导入地址表)以及hook的操作,有助于深入理解Windows程序设计原理,需要的朋友可以参考下
    2014-10-10
  • C++ 单例模式的详解及实例

    C++ 单例模式的详解及实例

    这篇文章主要介绍了C++ 单例模式的详解及实例的相关资料,这里对单例中的懒汉模式和饿汉模式进行实现和比较,需要的朋友可以参考下
    2017-07-07
  • C语言实现简单扫雷源码

    C语言实现简单扫雷源码

    这篇文章主要为大家详细介绍了C语言实现简单扫雷源码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-04-04
  • C语言中的sscanf()函数使用详解

    C语言中的sscanf()函数使用详解

    这篇文章主要介绍了C语言中的sscanf()函数使用详解,文中附加了一道相关的ACM题目进行补充巩固,需要的朋友可以参考下
    2015-08-08
  • C语言绘制曲线图的示例代码

    C语言绘制曲线图的示例代码

    这篇文章主要介为大家详细绍了如何使用C语言绘制统计图中的曲线图,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-02-02

最新评论