学过C语言的人都应该知道,我们所编辑的C语言程序是不能直接放到机器上运行的,它只不过是一个带".c"后缀的文件(也称为源代码)而已,需要经过一定的处理才能转换成机器上可运行的可执行文件。我们将对C语言的这种处理过程称为编译与链接。

编译就是把文本形式源代码翻译为机器语言形式的目标文件过程。

链接是把目标文件、操作系统的启动代码和用到的库文件进行组织最终形成可执行代码的过程。编译和链接的过程图解如下:

从图上可知,整个代码的编译过程分为编译和链接两个过程,其中编译对应图中的大括号括起来部分,其余则为链接过程。

其中编译过程又分为两个阶段:编译和汇编。

编译是读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,源文件的编译过程主要包含两个阶段:

第一个阶段是:预处理阶段,在正式的编译阶段之前进行。预处理阶段将根据已放置的文件中的预处理指令来修改源文件的内容。如#include指令就是一个预处理指令,他把文件的内容添加到.cpp文件中。这个在编译之前修改源文件的方式提供了很大的灵活性,以适应不同的计算机和操作系统环境的限制。一个环境所需要的可执行代码跟另一个环境所需要的可执行代码可能有所不同,因为可用的硬件体系结构和操作系统不同所致。在许多情况下(特别是在嵌入式开发中),可以把用于不同环境的代码放在同一个文件中,再在预处理阶段修改代码,使之适应环境。

预处理阶段主要是以下几方面的处理:

1)、宏定义指令,如#define、M a;

对于这种伪指令,预编译所要做的是将程序中的所有M用a来替换,一定要注意作为字符常量a则不被替换(因为已经是常量,其值已经是确定的)。与之相对应的还有#undef,则是将取消对某个宏的定义,使之在后面出现时再不被替换。

2)、条件编译指令,如#ifdef、 #ifndef、#else、#elif、#endif等。

这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。换言而之也就是预编译程序将根据有关的文件,将哪些不必要的代码过滤掉。

3)、头文件包含指令,如#include等。

在头文件中一般用伪指令#define定义了大量的宏(最常见的是字符常量),同时包含各种外部符号的声明。采用头文件的主要目的是为了使某些定义可以供多个不同的C源程序使用。因为当需要使用到这些定义的C源程序中,只需要加上一条#include语句即可,而不必在此文件中将这些定义重复一遍。预编译程序将把头文件中定义统统加入到它所产生的输出文件中,以供编译程序对之处理。在linux操作系统中包含到C源程序中的头文件可以是系统提供的,这些头文件一般被放在/usr/include/目录下。在程序中使用它们,#include要使用尖括号<>;另外开发人员也可以定义自己的头文件,这些文件一般与C源程序放在同一目录下,此时在#include中要使用""。

4)、特殊符号,预编译程序可以识别一些特殊的符号。

例如,在源程序中出现的LINE标识符将被解释为当前行号(十进制),FILE则被解释为当前被编译的C源程序的文件名称,FUNCTION则被解释为当前被编译的C源程序中的函数名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换,这些常常是配套使用,用来进行对程序的调试。

最后需要着重强调一点的是预处理阶段并不属于预编译过程,这经常是初学者容易搞错的。

预编译程序所完成的基本上是对源程序的"替换"工作。经过此替换后,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输出而被翻译成为机器指令。

第二个阶段是:编译、优化,经过预编译得到的输出文件中只有常量,一般都是一些指令。

编译程序所要做的工作就是通过词法分析和语法分析,在确认所有指令都是符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。

优化处理是编译系统中一项比较复杂高深的技术。它涉及到的问题不仅同编译技术有关,而且跟机器的硬件环境也有关。优化一部分是对中间代码的优化,这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的,这种优化与硬件环境有莫大的关系。

对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度消弱、变换循环控制条件、已知量的合并等)、复写传播及无用赋值的删去等等。

后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放的有关变量的值,以减少对于内存的访问次数(要知道内存资源对于计算机至关重要,控制好它可以大幅提高计算机的运算速度)。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。

汇编实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件由段组成。通常一个目标文件中至少有两个段:代码段和数据段;

代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

UNIX环境下主要有三种类型的目标文件:
1)可重定位文件:其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。
2)共享的目标文件:这种文件存放了适合于在两种上下文里链接的代码和数据。第一种是链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文件;第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。
3)可执行文件:它包含了一个可以被操作系统创建一个进程来执行的文件。汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这个就是链接程序的工作了。

链接过程是由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。 例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够按操作系统装入执行的统一整体。
根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:

    我们大家在编程过程中对“链接”这个词并不陌生,链接所解决的问题即是将我们自己写的代码和别人写的库集成在一起。链接可以分为静态链接与动态链接,下文将分别讲解这两种方式的特点与其区别。
  1. 静态链接

    • 特点:在生成可执行文件的时候(链接阶段),把所有需要的函数的二进制代码都包含到可执行文件中去。因此,链接器需要知道参与链接的目标文件需要哪些函数,同时也要知道每个目标文件都能提供什么函数,这样链接器才能知道是不是每个目标文件所需要的函数都能正确地链接。如果某个目标文件需要的函数在参与链接的目标文件中找不到的话,链接器就报错了。目标文件中有两个重要的接口来提供这些信息:一个是符号表,另外一个是重定位表。
    • 优点:在程序发布的时候就不需要的依赖库,也就是不再需要带着库一块发布,程序可以独立执行。
    • 缺点:
      1. 程序体积会相对大一些。
      2. 如果静态库有更新的话,所有可执行文件都得重新链接才能用上新的静态库。
  2. 动态链接
    • 特点: 在编译的时候不直接拷贝可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统,操作系统负责将需要的动态库加载到内存中,然后程序在运行到指定的代码时,去共享执行内存中已经加载的动态库可执行代码,最终达到运行时连接的目的。
    • 优点: 多个程序可以共享同一段代码,而不需要在磁盘上存储多个拷贝。
    • 缺点: 由于是运行时加载,可能会影响程序的前期执行性能。
          上面的文章多次提到库(lib)这个概念,所谓的库就是一些功能代码经过编译连接后的可执行形式。
             大家在Windows平台上见到的.dll文件和linux平台下so动态库都为输入库。
             库也有静态lib和动态lib之分:
  1. 静态lib将导出声明和实现都放在lib中。编译后所有代码都嵌入到宿主程序。
  2. 动态lib相当于一个h文件,是对实现部分(.dll文件)的导出部分的声明。编译后只是将导出声明部分编译到宿主程序中,运行时候需要相应的dll文件支持。

我们在linux使用的gcc编译器便是把以上的几个过程进行捆绑,使用户只使用一次命令就把编译工作完成,这的确方便了编译工作,但为了初学者了解编译过程,下图便给出了gcc代理的编译过程:

从上图可以看到:
a、预编译
将.c 文件转化成 .i文件
使用的gcc命令是:gcc –E
对应于预处理命令cpp

b、编译
将.c/.h文件转换成.s文件
使用的gcc命令是:gcc –S
对应于编译命令 cc –S

c、汇编
将.s 文件转化成 .o文件
使用的gcc 命令是:gcc –c
对应于汇编命令是 as

d、链接
将.o文件转化成可执行程序
使用的gcc 命令是: gcc
对应于链接命令是 ld

总结起来编译过程就上面的四个过程:预编译、编译、汇编、链接。了解了这四个过程中所做的工作,对我们理解头文件、库等的工作过程是有帮助的,而且清楚的了解编译链接过程还对我们在编程时定位错误,以及编程时尽量调动编译器的检测错误会有很大的帮助的。

转载:http://blog.csdn.net/q_l_s/article/details/51314663

C语言的编译链接过程详解相关推荐

  1. Linux下Nginx编译安装过程详解

    Linux下Nginx编译安装过程详解 一.Nginx介绍 二.Nginx源码下载 1.打开Nginx官网 2.下载官网的源码包 三.Nginx源码安装 1.解压源码包 2.安装开发包组及环境 3.编 ...

  2. C语言的编译链接过程的介绍

    发布时间: 2012-11-08 10:17    作者: 未知    来源: 51Testing软件测试网采编 字体:  小  中  大  | 上一篇 下一篇 | 打印  | 我要投稿  | 推荐标 ...

  3. U-Boot 之一 零基础编译 U-Boot 过程详解、Image 镜像介绍及使用说明、DTB 文件使用说明

      最近,工作重心要从裸机开发转移到嵌入式 Linux 系统开发,在之前的博文 Linux 之八 完整嵌入式 Linux 环境.(交叉)编译工具链.CPU 体系架构.嵌入式系统构建工具 中详细介绍了嵌 ...

  4. U-Boot 之一 零基础编译 U-Boot 过程详解 及 编译后的使用说明

      在之前的博文 Linux 之八 完整嵌入式 Linux 环境介绍及搭建过程详解 中我们说了要一步步搭建整个嵌入式 Linux 运行环境,今天就开始编译 U-Boot.我所使用的硬件平台及整个要搭建 ...

  5. MySQL8.0二进制免编译部署过程详解(二)

    一.背景介绍 本文主要介绍MySQL二进制免编译软件包的安装过程详解,之所以选择二进制安装包部署MySQL8.0,是因为官方版本已经内置所有功能,在安装的时候可以指定数据库安装路径. 目前官网MySQ ...

  6. C/C++编译和链接过程详解 概述 (重定向表,导出符号表,未解决符号表)

    详解link  有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错 ...

  7. python爬虫获取下一页url_Python爬虫获取页面所有URL链接过程详解

    如何获取一个页面内所有URL链接?在python中可以使用urllib对网页进行爬取,然后利用Beautiful Soup对爬取的页面进行解析,提取出所有的URL. 什么是Beautiful Soup ...

  8. python爬虫之js链接跳转抓取_Python爬虫获取页面所有URL链接过程详解

    如何获取一个页面内所有URL链接?在Python中可以使用urllib对网页进行爬取,然后利用Beautiful Soup对爬取的页面进行解析,提取出所有的URL. 什么是Beautiful Soup ...

  9. c语言编辑编译链接,C语言的“编译、链接”

    我们写的代码,仅仅是文本文件(txt),不管后缀怎么变,终究是文本文件,计算机只能显示,不能做事(执行). 文本文件通过"编译.链接",成为可执行文件(windows下是exe): ...

最新文章

  1. 不同版本gene ID比较
  2. 《云计算》学习笔记1
  3. 《分布式系统:概念与设计》一1.3 分布式系统的趋势
  4. XCTF_Web_新手练习区:backup
  5. linux查看运行钟的tomcat,linux查看tomcat启动运行日志
  6. Java基础中按值传递和引用传递详解
  7. html 的layer的属性,layer属性
  8. python3记录(3)-os.walk() 方法
  9. Microsoft office word2003出现发送错误报告怎么办
  10. STM32固件库下载教程
  11. 织梦采集侠教程设置免费版本
  12. 【物理】半导体物理 西安电子科技大学 柴常春等主讲-[笔记P11-P14]
  13. 【Linux】 Debian 系 Linux 安装搜狗输入法
  14. 值得收藏:一份非常完整的MySQL规范
  15. 如何购买华为服务器+云计算特点
  16. 华大单片机HC32L130X
  17. 计算机管理员工作目标任务书,毕业论文任务书中主要任务及目标怎么写
  18. Magento2.1x常见报错处理
  19. linux编译jsoncpp
  20. pytorch repeat 使用详解

热门文章

  1. (转)Linux下MatlabCompilerRuntime的安装和使用
  2. CodeIgniter学习笔记一:基本结构、控制器、视图、超级对象、数据库
  3. RH413-测试文件的a属性
  4. 运行txt文本也可能中毒!微软急修“文本病毒”漏洞
  5. 572. 另一个树的子树
  6. Linux命令之du命令df命令
  7. java开发手册:线程池不允许使用 Executors 去创建
  8. vue全局组件与局部组件
  9. Spark On K8S与Spark On YARN方案相比之劣势
  10. spark on yarn相关脚本整理20210524