C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作系统的启动代码和用到的库文件进行组织,形成最终生成可执行代码的过程。过程图解如下:
从图上可以看到,整个代码的编译过程分为编译和链接两个过程,编译对应图中的大括号括起的部分,其余则为链接过程。

1. 编译过程

编译过程又可以分成两个阶段:编译和汇编。

编译

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

编译预处理

读取c源程序,对其中的伪指令(以# 开头的指令)和特殊符号进行处理。
伪指令主要包括以下四个方面:
1) 宏定义指令,如# define Name TokenString,# undef等。
对于前一个伪指令,预编译所要做的是将程序中的所有Name用TokenString替换,但作为字符串常量的 Name则不被替换。对于后者,则将取消对某个宏的定义,使以后该串的出现不再被替换。
2) 条件编译指令,如# ifdef,# ifndef,# else,# elif,# endif等。
这些伪指令的引入使得程序员可以通过定义不同的宏来决定编译程序对哪些代码进行处理。预编译程序将根据有关的文件,将那些不必要的代码过滤掉。
3) 头文件包含指令,如# include “FileName” 或者# include < FileName> 等。
在头文件中一般用伪指令# define定义了大量的宏(最常见的是字符常量),同时包含有各种外部符号的声明。
采用头文件的目的主要是为了使某些定义可以供多个不同的C源程序使用。因为在需要用到这些定义的C源程序中,只需加上一条# include语句即可,而不必再在此文件中将这些定义重复一遍。预编译程序将把头文件中的定义统统都加入到它所产生的输出文件中,以供编译程序对之进行处理。
包含到c源程序中的头文件可以是系统提供的,这些头文件一般被放在/ usr/ include目录下。在程序中# include它们要使用尖括号(< >)。另外开发人员也可以定义自己的头文件,这些文件一般与c源程序放在同一目录下,此时在# include中要用双引号(”“)。
4) 特殊符号,预编译程序可以识别一些特殊的符号。
例如在源程序中出现的LINE标识将被解释为当前行号(十进制数),FILE则被解释为当前被编译的C源程序的名称。预编译程序对于在源程序中出现的这些串将用合适的值进行替换。
预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,但内容有所不同。下一步,此输出文件将作为编译程序的输入而被翻译成为机器指令。

编译、优化阶段

经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main, if , else , for , while , { , } , + , - , * , \ 等等。
编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。
优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。
对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。
后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。
经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。

汇编

汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。
目标文件由段组成。通常一个目标文件中至少有两个段:
1) 代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
2) 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。
UNIX环境下主要有三种类型的目标文件:
1) 可重定位文件
其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。
2) 共享的目标文件
这种文件存放了适合于在两种上下文里链接的代码和数据。
第一种是链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文件;
第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。
3) 可执行文件
它包含了一个可以被操作系统创建一个进程来执行之的文件。
汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这个就是链接程序的工作了。

2. 链接过程

由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。
例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。
根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:
1) 静态链接
在这种链接方式下,函数的代码将从其所在的静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。
2) 动态链接
在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。
对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

3.GCC的编译链接

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

1) 预编译
将.c 文件转化成 .i文件
使用的gcc命令是:gcc –E
对应于预处理命令cpp
2) 编译
将.c/.h文件转换成.s文件
使用的gcc命令是:gcc –S
对应于编译命令 cc –S
3) 汇编
将.s 文件转化成 .o文件
使用的gcc 命令是:gcc –c
对应于汇编命令是 as
4) 链接
将.o文件转化成可执行程序
使用的gcc 命令是: gcc
对应于链接命令是 ld
总结起来编译过程就上面的四个过程:预编译处理(.c) --> 编译、优化程序(.s、.asm)--> 汇编程序(.obj、.o、.a、.ko) --> 链接程序(.exe、.elf、.axf等)。

4.总结

C语言编译的整个过程是非常复杂的,里面涉及到的编译器知识、硬件知识、工具链知识都是非常多的,深入了解整个编译过程对工程师理解应用程序的编写是有很大帮助的,希望大家可以多了解一些,在遇到问题时多思考、多实践。
一般情况下,我们只需要知道分成编译和链接两个阶段,编译阶段将源程序(*.c) 转换成为目标代码(一般是obj文件,至于具体过程就是上面说的那些阶段),链接阶段是把源程序转换成的目标代码(obj文件)与你程序里面调用的库函数对应的代码连接起来形成对应的可执行文件(exe文件)就可以了,其他的都需要在实践中多多体会才能有更深的理解。

转载于:https://www.cnblogs.com/Stephen-Qin/p/10466712.html

[转]C++编译链接过程详解相关推荐

  1. C语言的编译链接过程详解

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

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

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

  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. U-Boot 之三 U-Boot 源码文件解析及移植过程详解

      在之前的博文 Linux 之八 完整嵌入式 Linux 环境介绍及搭建说明 中我们说了要一步步搭建整个嵌入式 Linux 运行环境.我所使用的硬件平台及整个要搭建的嵌入式 Linux 环境见博文 ...

  10. c语言程序链接过程,C语言简明教程(二):C程序编译链接过程和实例对照详解...

    不像高级编程语言,在C语言开发中,了解其编译链接过程显得相对重要,因为C语言是较为底层的语言,很多时候我们调试C程序或者解决其它问题都可能会涉及到C编译链接的相关知识,例如编译动态库或者静态库.下面我 ...

最新文章

  1. Nagios 监控 ESXi
  2. 最优化算法python实现篇(4)——无约束多维极值(梯度下降法)
  3. 数据结构与算法之美-哈希算法
  4. 使用aconda3-5.1.0(Python3.6.4) 搭建pyspark远程部署
  5. matlab sort descend,详解Matlab中 sort 函数用法
  6. java中TreeSet集合如何实现元素的判重
  7. Design-patterns-JS:用JavaScript实现23种设计模式
  8. 想到Exchanger N parties的一种用法
  9. Linux彻底删除mysql
  10. java 如何去掉http debug日志_Spring Boot手把手教学(3):从零配置logback日志
  11. python open打开文件的方式
  12. robot脚本编写规范
  13. android手机计算器代码下载,Android计算器 例子源码下载
  14. 256K,320K及以上Nero AAC,QAAC,FAAC,MP3,OGG等主流有损音频格式横评
  15. c语言爱心灯制作步骤,如何使用LED和555定时器制作闪烁爱心LED灯
  16. iOS10开发哪些坑
  17. 一招连环追销,让顾客在第一次成交中买多个产品,客单价提高十倍
  18. opencv 棋盘标定
  19. CSS实战笔记(十二) 图片等比例缩放
  20. 为什么越来越多的访问学者去加拿大访学?

热门文章

  1. 几个北大和南开学霸公众号,值得学习
  2. R工程化—Rest API 之plumber包
  3. 统计挖掘那些事(八)—— 分层抽样与交叉验证
  4. BNUOJ 34978 汉诺塔 (概率dp)
  5. 深入Node.js的模块机制
  6. wex5中集成的mysql数据库 打开时一闪而过 报错
  7. C#小方法PadLeft 和 PadRight
  8. js自写字符串 append 方法
  9. [Flex]浅析Mate flex framework在实际项目中的应用(二)
  10. Hibernate 注解方式