可执行程序的组成

上一节分析了C语言应用程序中各段的情况,实际的C语言可执行程序,将由各个文件经过连接生成。目标文件是由每一个C语言源程序(*.c)经过编译器生成,目标文件(.o)的主要组成部分即代码段、只读数据段和读写数据段三个段。未初始化数据段、堆和栈不会占用目标文件的空间。

        可执行程序是由各个目标文件经过连接而成。其主体部分依然是代码段、只读数据段和读写数据段,这三个段由各个目标文件(.o)经过“组合”而成。C语言目标文件到可执行程序的连接如图13-2所示。

        连接器将根据连接顺序将各个文件中的代码段取出,组成可执行程序的代码段,只读数据段和读写数据段也是如此。在连接过程中,如果出现符号重名、符号未定义等问题,将会产生连接错误。如果连接成功,将会生成一个统一的文件,这就是可执行程序。

        由连接器生成的可执行程序包含了各个目标文件的各个段的内容,并且会附加可执行程序的头信息。在可执行程序中,各个目标文件的代码段、只读数据段、读写数据段经过了重新的排列组合。因此,在最终的可执行程序中,已经没有了各个目标文件的概念。

        值得注意的是,在连接的过程中,连接器可以得到未初始化数据段的大小,它也是各个目标文件的各个未初始化数据段数据段之和,但是这个段是不会影响可执行程序大小的。从C语言使用的角度,读写数据段和未初始化数据段都是可读写的。实质上,在目标文件(*.o)中未初始化数据段和读写数据段的区别也在于此:读写数据段占用目标文件的容量,而未初始化数据段只是一个标识,不需要占用实际的空间。
 
图13-2  C语言目标文件到可执行程序的连接

        例如,在某一个C语言的源程序文件中,具有以下的内容:
        static char bss_data[2048];
        static char rw_data[1024] = {""};

        以上定义了两个静态数组,由于bss_data没有初始化,是一个未初始化数据段的数组,编译器只需要标识它的大小即可,而rw_data已经有了一定的初始化数据(即使这个初始化数据没有实际的内容),它建立在已初始化数据段之上,编译器需要在读写数据段内为其开辟空间并赋初值。因此,在生成目标文件的时候,由于rw_data[1024]的存在,目标文件的大小将增加1024字节,而bss_data [2048]虽然定义了2048字节的数组,目标文件的大小并不会因此而增加。

        连接器在处理的过程中,会将各个目标文件读写数据段组合成可执行程序的读写数据段,类似rw_data等内容都会被组合,因此可执行程序中读写数据段的大小会等于各个目标文件读写数据段之和。对于bss_data等未初始化数据段上的变量,连接器也将各个目标文件中的信息相加得到可执行程序的未初始化段的大小,但是这个段同样不会占用可执行文件的空间。

        在C语言中,读写数据段和未初始化数据段都包含了以下几种情况:整个程序的全局变量、单个文件内使用的全局变量(函数外部用static修饰的)、局部静态变量(函数内部用static修饰的)。对于这几种变量,连接器都会按照相同的方式进行组合。

         知识点 :在连接过程中,C语言的各个目标文件的代码段、只读数据段和读写数据段将组合成可执行程序的这三个段,未初始化数据段只有在运行时才会产生。

各个目标文件的关系

        程序中通常会有大量的函数调用,这些被调用的函数只要有声明(而不需要定义实现),编译器就可以成功处理。在生成可执行文件的过程中,连接器将各个可执行程序的代码段组合到一起,而有函数调用的地方还需要找到真正的函数定义才可以完成连接。因此,函数的定义和调用者可以在一个代码段内,也可以在不同的代码段内。连接器会根据需要根据实际的情况修改编译器生成的机器代码,完成正确的跳转。
        函数跳转的连接过程如图13-3所示。
 
图13-3  函数跳转的连接

        与函数跳转类似的是全局变量的访问,在C语言编译的过程中,程序可以访问用extern声明的外部全局变量,在连接的过程中,连接器需要找到实际变量在数据段中的位置,完成正确的变量访问。
        程序中全局变量的连接如图13-4所示。
 
图13-4  全局变量的连接

        对于可执行文件的生成,其主要的工作是组合各个目标文件中的三个段。还将包含一些其他的过程。首先,所有的可执行程序都需要指定一个入口,在C语言中入口即main函数,在一个C语言的应用程序各个源文件中,只能包含一个main函数。其次,不同系统所使用的可执行程序可能包含不同的头信息,头信息是在主要段之外附加的信息,可以供操作系统加载可执行程序的时候使用。        

         知识点 :在连接过程之前,各个源文件生成目标文件相互没有关系。在连接之后,各目标文件函数和变量可以相互调用和访问,从而被联系在一起。
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值