链接和编译

在开始学习C语言编程时,我们所写的第一份代码应该就是:

#include <stdio.h>

int main()
{
    // 打印
    printf("Hello World! \n");
    
    return 0;
}

最初我们只能知道这段代码执行结果是打印一句: Hello World!

但背后具体运行过程是怎样的呢?

    通过学习,本篇尝试对此过程进行解释说明,如有不足希望指出。

编译过程

      我们都知道我们所写的代码是供给我们程序开发人员阅读的,而真正机器能理解的只有二进制:0、1。所以我们需要将这些代码翻译成机器可执行的二进制代码,这个过程是交由编译系统来完成。编译系统呢是由:预处理器、编译器、汇编器、链接器组成。

       这四部分就对应程序编译的四步骤:预编译(Preprocessing)编译(Compiler)汇编(Assembler)链接(Linker)

GCC编译过程

                                                                        

预编译

      预编译阶段所要做的是对源程序的"替代"工作,就是将以"#"开头的指令替换为所期望或是所需要的文件/代码。

      处理规则:

  • 宏定义指令。对于"#define"开头语句,所要做的就是删除所有的"#define",再进行宏替换。"#undef"则将取消对某个宏的定义,使之后出现目标串不再被替换。
  • 条件编译指令,#ifdef、#ifndef、#else、#elif、#endif 等。通过定义不同的宏来挑出一些符合条件的代码交给下一步的编译阶段来处理。
  • 头文件包含指令,"#include"开头的语句,将被包含的文件插入到该预编指令位置。(这个过程可以递归进行)。
  • 删除所有注释行。
  • 特殊符号行号文件名标识,为了便于编译时的调试(如编译出错可以返回行号和出错信息)。
  • 保留所有#pragma 编译器指令,因为编译器需要使用。

     通过预编译将我们的.c文件转化为.i文件。在gcc下可以通过下示方式得到预处理后的.i文件:

$gcc -E hello.c -o hello.i 

编译

      编译过程就是将预处理后的文件进行一系列扫描、词法分析、语法分析、语义分析及优化后产生相应的汇编代码文件,这个过程是整个程序最核心、最复杂的部分。

  • 扫描:读取源程序代码;
  • 词法分析:将扫描的代码序列分割成一系列记号,根据相应词法规则进行分类;
  • 语法分析:识别短语和句型的语法属性,同时确定运算符号的优先级和含义;
  • 语义分析:确定表达式(包括符号和数字)的类型;
  • 代码优化:对目标代码进行优化,比如选择合适寻址方式,使用位移运算代替乘除运算;
  • 代码生成:生成汇编代码文件。

 现在版本的gcc会将预编译和编译合起来处理,通过使用一个ccl的程序来完成。

 也可以使用如下命令得到汇编代码文件:

gcc -S hello.c -o hello.s

  实际上gcc命令只是后台程序的包装,它会根据不同参数要求调用预编译编译程序ccl、汇编器as、链接器ld。

 

汇编

      汇编过程就是将汇编代码转为机器可执行指令,几乎每一条汇编代码都对应一条机器指令。因此此过程主要是根据汇编指令和机器指令对照表进行翻译。

       我们可以通过如下指令获取目标文件:

gcc -c hello.s -o hello.o

目标文件:由编译所生成的文件,以机器码的形式包含了编译单元里所有的代码和数据,以及一些其他的信息。

 

链接

     在实际软件工程开发过程中,软件规模非常大,所以不能像学习中一样将所有代码放在一个文件中执行。所以为了方便阅读、理解、调试、维护通常会分为许多模块进行处理,这些模块间相对独立又相互依赖。

     如果说某一模块的变量或是所需调用的函数在其他模块中,那么怎么能得到所需内容的地址?链接就起这种作用。链接就是将不同模块组装起来(类似于拼拼图一样),将各个模块间相互引用部分处理好,使得各模块间能正确衔接。

      链接过程主要包括:地址和空间分配、符号决议、重定位。

      链接库的方式可分为:

(1)静态链接  在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

 (2)动态链接  在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。 

     对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

 

以上内容参考《程序员自我修养》

https://blog.youkuaiyun.com/wyb19890515/article/details/7211006

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值