菜鸟在C语言编译,链接时可能遇到的两个问题

转载 2014年10月31日 22:11:41

最近在看 CSAPP (Computer Systems A Programmers Perspective 2nd) 的第七章 链接。学到了点东西,跟大家分享。下文中的例子都是出自CSAPP第七章。

另外,也可以结合酷壳上的这篇文章和之后的留言来看本文,理解会更加深刻一些。
1.问:如果在不同的C源文件中定义了相同名称的全局变量会有什么样的后果呢?

比如下面的这种情况:

有两个源文件foo3.c和bar3.c:
foo3.c

复制代码
#include <stdio.h>
void f(void);

int x = 15213;

int main()
{
    f();
    printf("x = %d\n", x);
    return 0;
}
复制代码

bar3.c

int x;

void f()
{
    x = 15212;
}

要解答这个问题,就得知道链接的时候,链接器(Linker)是如何解析多处定义的全局符号的:[ CSAPP 7.6.1]

复制代码
在编译时,编译器输出每个全局符号(global symbols)给汇编器,这些符号要么是强(strong symbol)符号,要么是弱(weak symbol)符号。
函数和已初始化的全局变量是强符号,未初始化的全局变量是弱符号。 根据强弱符号的定义,Unix链接器使用下面的规则来处理多处定义的符号: 规则1:不允许有多个强符号【
否则,链接的时候会出错:multiple definition of 'xx'】。 规则2:如果有一个强符号和多个弱符号,那么选择强符号 规则3:如果有多个弱符号,那么从这些弱符号中任意选一个
复制代码

所以根据规则2,bar3.c中的x是弱引用,在链接时,编译器会悄悄地会认为bar3.c处引用的是foo3.c中定义的x,所以在main调用f()之后,x = 15212;

小伙伴们如果看明白了,可以想想在IA32/Linux机器上,下面这段代码的输出是什么?

foo5.c

复制代码
#include <stdio.h>
void f(void);

int x = 15213;
int y = 15212;

int main()
{
    f();
    printf("x = 0x%x y = 0x%x \n", x, y);
}
复制代码

bar5.c

double x;
void f()
{
    x = -0.0
}

 

对于上述C语言中多处定义的全局符号问题,有没有什么解决方案呢?

个人认为:
        1.尽量减少全局变量的使用(有好多坑),凡是别的文件里用不着的全局变量,都加上static来限定其作用域都在本模块内,这样这个变量也就不是全局符号了,是本地符号。
        2.可以在gcc的命令行里加上 -fno-common 参数,当遇到多重定义的全局变量时,会报错  
    
2.在编译源代码的时候,是否会遇到链接器死活就是提示错误: undefined reference to 'foo'。仔细确认了好几遍,发现包含了这个函数需要的库的名称,也指定了库的路径,也通过命令:nm -s libfoo.a 看到libfoo静态库里是有foo这个符号的,但链接阶段就是会报错。【我当时遇到这个问题时,百思不得其解,快要跳脚骂娘了。后来还是找老鸟同事搞定的。】

上述这种情况,很有可能是命令行里,指定编译所需的静态链接库时,链接库的顺序有问题。举个例子:
        foo.c 调用了libx.a和libz.a中的函数,而这两个库又需要调用liby.a中的函数,那么在,命令行中,libx.a和libz.a必须在liby.a之前,否则就会出现找不到符号定义的情况。为什么呢?请看解释[CSAPP 7.6.3]:      

复制代码

在符号解析的阶段,链接器从左到右按照文件在编译器驱动程序命令行上出现的相同顺序来扫描可重定位目标文件和存档文件。(驱动程序自动将命令行中所有的.c文件翻译为.o文件)在这次扫描中,链接器维持一个可重定位目标文件的集合 E,这个集合中的文件会被合并起来形成可执行文件,和一个未解析的符号(也就是,引用了但是尚未定义的符号)集合U,以及一个在前面输入文件中己定义的符号集合D。初始地,E, U和D 都是空的。 对于命令行上的每个输入文件f,链接器会判断f是一个目标文件(object file)还是一个存档文件(archive)。如果f是一个目标文件,那么链接器把f添加到E,修改U和D来反映f中的符号定义和引用,并继续下一个输入文件。如果f是一个存档文件,那么链接器就尝试匹配U中未解析的符号和由存档文件成员定义的符号。如果某个存档文件成员m,定义了一个符号来解析U中的一个引用,那么就将m加到E中,并且链接器修改U和D来反映m中的符号定义和引用。对存档文件中所有的成员目标文件都反复进行这个过程,直到U和D都不再发生变化。在此时,任何不包含在E 中的成员目标文件都被丢弃,而链接器将继续到下一个输入文件。 如果当链接器完成对命令行上输入文件的扫描后,U是非空的,那么链接器就会输出一个错误并终止。否则,它会合并和重定位E中的目标文件,从而构建输出的可执行文件。

复制代码

上面这一大段挺绕的,简单来说,就一句话:如果在命令行中,定义一个符号的库出现在引用这个函数/变量的目标文件之前,那么引用就不能被解析,链接会失败。就像上面的例子。

update: 2013/10/20

另外,如果静态库之间不是相互独立的,也有相互引用,那么必须得正确安排好顺序。比如,foo.c调用libx.a和libz.a中的函数,而这两个库又调用 liby.a中的函数。那么命令行中libx.a和libz.a必须在liby.a之前:

gcc foo.c libx.a libz.a liby.a

如果需要满足依赖的需求,可以在命令行上重复库。比如,foo.c 调用 libx.a,该库又调用 liby.a,而 liby.a 又调用libx.a。那么libx.a 必须在命令行上重复出现:

gcc foo.c libx.a liby.a libx.a     

如果还有不太明白的地方,可以去看看CSAPP的第七章。

强烈推荐大家有时间读读CSAPP这本书啊,收获会很大的。附上 老赵书托(3):深入理解计算机系统 
   

分类: C

学习C语言常见的几个问题及解决方法

所有初学者面临的第一个问题便是:如何选择教材。好的开始是成功的一半,选择一本优秀的教材是事半功倍的关键因素。不幸的是,学校通常会帮你指定一本很差劲的C语言课本;而幸运的是,你还可以再次选择。   ...
  • bentengdehe
  • bentengdehe
  • 2016年04月28日 16:52
  • 1509

用keil软件编写C语言排序程序

用keil软件编写一个排序子程序,用冒泡法将内部RAM 50H~5BH中的单字节无符号的正整数,按从小到大的次序进行排列。本题目比较难的地方是硬性指定存储单元的地址。以下程序可以满足要求。#inclu...
  • baidu_33836580
  • baidu_33836580
  • 2016年01月25日 11:27
  • 1790

C语言中遇到的问题及解决方法

2012年5月5号; 问题:for语句和while语句后面有; 的情况。相当于 这是执行空循环,i会累加,但啥都不执行。直到不满足条件为止。 for(;;) ; while() ; 20...
  • frostwong
  • frostwong
  • 2012年05月06日 10:36
  • 1149

一些面试时候可能遇到的问题

1、请你自我介绍一下你自己,   回答提示:一般人回答这个问题过于平常,只说姓名、年龄、爱好、工作经验,这些在简历上都有,其实,企业最希望知道的是求职者能否胜任工作,包括:最强的技能、最深入研究的知...
  • baidu_32173921
  • baidu_32173921
  • 2017年05月08日 21:05
  • 297

12月15日做图书管理系统所遇到的问题

做图书管理系统所遇到的问题
  • xiaowenwenwenwenwen
  • xiaowenwenwenwenwen
  • 2016年12月15日 16:08
  • 562

【编译器】使用Visual Studio编写C语言程序(附若干错误解决方案)

本文通过图文结合的方式,介绍了Windows10下怎样使用Visual Studio 2015编写C语言程序,并附有若干问题的解决方案。文章将会不定期更新。欢迎大家与我交流。...
  • bit_thinkind
  • bit_thinkind
  • 2015年11月25日 21:46
  • 1243

c语言中遇到的问题

warning C4013: 'getch' undefined; assuming extern returning int或getchar  2010-09-28 09:23:29|  分类:C...
  • juanzi704
  • juanzi704
  • 2012年12月20日 20:36
  • 569

实践中遇到的问题总结

body在默认情况下占据了当前可视窗口的整个高度和宽度 如果想让盒子中的内容垂直居中,可以让盒子的line-height和盒子的高度相等。 默认情况下,盒子的高度和宽度值都是auto,但空盒子是看不见...
  • VivianHope
  • VivianHope
  • 2016年05月29日 11:22
  • 7275

android开发中遇到的问题汇总【七】

更多问题请关注 [android开发遇到问题点滴](https://github.com/ayyb1988/android-issues)...
  • u011570979
  • u011570979
  • 2015年05月19日 12:37
  • 2354

网站制作中可能遇到的问题

     随着互联网技术的革新,传统的技术格局逐渐被打破,更多更新的技术开始在该行业得到使用。网站呈现出来的不只是内容,人们越来越重视网站的效率。页面刷新的速度和视觉效果等成为衡量一个站点质量好坏的重...
  • xiaoK
  • xiaoK
  • 2006年04月11日 12:26
  • 3222
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:菜鸟在C语言编译,链接时可能遇到的两个问题
举报原因:
原因补充:

(最多只允许输入30个字)