C进阶—详解编译、链接
被隐藏了的过程
现如今在流行的集成开发环境下我们很少需要关注编译和链接的过程,而隐藏在程序运行期间的过程可不简单,即使使用命令行来编译一个源代码文件,简单的一句"gcc hello.c"命令就包含了非常复杂的过程。

1 #include<stdio.h>
3 int main()
4 {
5 printf("Hello word\n");
6 return 0;
7 }


在Linux系统下使用gcc编译程序时只须简单的命令:
$gcc hello.c
$/a.out
Hello word
不管哪种编辑器,以上过程可分为4个步骤,分别是预编译(Prepressing)、编译(Compilation)、汇编(Assembly)、链接(Linking)。

GCC 编译过程分解
预编译
首先是将源代码文件hello.h和相关的头文件,如stdio.h等被编译器Cpp预编译成一个.i文件。主要处理那些源文件中以“#”开始的预编译指令,如“#include"、”#define“等,主要规则如下:
将所有的”#define“删除,并且展开所有的宏定义。
处理所有条件预编译指令,比如”#if”、”#ifdef“、”#elif“等。
处理”#include“预编译命令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。
删除所有的注释”//“和”/**/“。
添加行号和文件名标识,比如#2”hello.c“2,以便于编译器产生调试用时的行号信息及用于编译时产生编译错误或警告时能显示行号。
保留所有的#pragma编译器指令,因为预编译器需要用他们。
编译
编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析、生成汇编文件,这个过程是是整个程序构建的核心部分,也是最复杂的部分之一。gcc将预编译和编译合并成一个步骤,使用如下命令:
$gcc -s hello.c -o hello.s
可得到会变输出文件 hello.s 。实际上gcc这个命令只是这些后台程序的包装,它会根据不同的参数要求去调用预编译编译程序cc1、汇编器as、链接器ld。
编译器职责
词法分析 经过预编译的源代码程序被输入到扫描器(Scanner),扫描器对其进行简单的词法分析,运用一种类似于有限状态机的算法将源代码的字符列分割成一系列的记号。如:关键字、标识符、字面量(包含数字、字符串等)和特殊符号(如加号、等号)。在标别记号的同时扫描器也完成了其他如将标识符存放到符号表,将数字、字符串常量存放到文件表等的工作,以备后面的步骤使用。(lex程序可实现词法扫描,按照一定的词法规则完成标别记号等功能,所以无需为每个编译器开发一个独立此法扫描器,而是根据需要改变语法规则即可。)
语法分析 语法分析器采用上下文无关语法的分析手段对扫描器产生的记号(Token)进行语法分析,从而生成语法树,即一表达式为节点的树。同时很多运算符的含义和优先级也被确定下来。编译器也会报告出语法分析阶段的错误。(如词法分析有像lex一样语法分析有现成工具ycc,它可根据语法规则对输入的记号序列构建出一颗语法树。对不同的编程语言只须改变语法规则即可。)
语义分析 语义分析由语义分析器完成,它所能分析的语义是静态语义,即编译期间可以确定的语义,运行期间才能确定的语义是指动态语义。静态语义通常包括生命和类型匹配,类型转换,如浮点型到整型转换。经过语义分析以后整个语法树都被标识了类型,如果有些类型需要做隐式转换,语义分析程序会在语法树中插入相应的转换节点。语义分析器对符号表里的符号类型也做了更新。语法分析仅仅完成对表达式语法层面的分析, 该语句是否有意义不进行检测。
符号汇总 源码优化器会在源代码级别进行优化,它往往将整个语法树转换成中间代码,它是语法树的顺序表示,已非常接近目标代码。中间代码有多种类型,常见的有三地址码,P-代码。中间代码使得编译器可分成前端和后端,前段即产生中间代码,后端将中间代码转换成目标机器代码。编辑器主要包括代码生成器和目标代码生成器。代码生成器将中间代码转换成目标机器代码。目标代码优化器再对其进行优化,如选择合适的寻址方式、使用位移来代替乘法运算、删除多余指令等。
汇编
汇编器是将汇编代码变成机器可以执行的指令,每一条汇编指令几乎都对应一条机器指令,根据其对照表一一翻译即可。目标文件中还包括链接是所需要的一些调试信息: 比如符号表、 调试信息、 字符串等。
链接
人们把每个源代码模块独立的进行编译,然后按照需要将它们组装起来,这个组装的过程就是链接(Linking)。
未解决的符号表: 列出本单元里有引用但是不在本单元定义的符号以及地址。导出符号表: 本单元中定义的一些符号(全局、静态变量和函数) 和地址的映射表。地址重定向表: 提供了本编译单元所有对自身地址的引 用记录。连接器的工作顺序:当连接器链接的时候, 首先决定各个目标文件在最终可执行文件里的位置。然后访问所有目标文件的地址重定义表, 对其中记录的地址进行重定向 (加上一个偏移量, 即该编译单元在可执行文件上的起始地址) 。然后遍历所有目标文件的未解决符号表, 并且在所有的导出符号表里查找匹配的符号, 并在未解决符号表中所记录的位置上填写实际地址。最后把所有的目标文件的内容写在各自的位置上,和库(Library)一起链接,形成最终的可执行文件。
总结:

C进阶—详解编译、链接的更多相关文章
- Jenkins企业应用进阶详解(一)
Jenkins企业应用进阶详解(一) 链接:https://pan.baidu.com/s/1NZZbocZuNwtQS0eGkkglXQ 提取码:z7gj 复制这段内容后打开百度网盘手机App,操作 ...
- L011系统文件属性知识进阶详解小节
L011系统文件属性知识进阶详解小节 这节课的内容相对来说较少,一上午加中午就听完了,现在总结一下,最后会有一个相关的面试题. 首先先附上一张图: 今天学习主要跟①和②有关,①为Inode 号 ②为文 ...
- Linux 链接详解----静态链接实例分析
由Linux链接详解(1)中我们简单的分析了静态库的引用解析和重定位的内容, 下面我们结合实例来看一下静态链接重定位过程. /* * a.c */ ; void add(int c); int mai ...
- shell脚本进阶 详解及其实例(一)
v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VM ...
- 【FPGA篇章六】FPGA编译向导:详解编译预处理功能
欢迎大家关注我的微信公众账号,支持程序媛写出更多优秀的文章 Verilog HDL语言和C语言一样也提供了编译预处理功能. Verilog HDL允许在程序中使用特殊的编译预处理语句. 在编译时,通常 ...
- Linux ll查看文件属性详解-软硬链接详解
Linux文件属性及类型 [root@localhost ~]# ll anaconda-ks.cfg 文件类型 权限 硬连接数 文件的大小 文件的创建,修改时间 - rw-------. 1 roo ...
- T-SQL查询进阶--详解公用表表达式(CTE)
简介 对于SELECT查询语句来说,通常情况下,为了使T-SQL代码更加简洁和可读,在一个查询中引用另外的结果集都是通过视图而不是子查询来进行分解的. 但是,视图是作为系统对象存在数据库中,那对于结果 ...
- pm2 start命令进阶详解
在node的世界里面,并不存在nginx或者apache,甚至tomcat这种东东.一个node,本身就用几行代码,就可以启动个server进程,监听个端口,为大家提供web服务.这和传统的网站代码的 ...
- Python进阶——详解元类,metaclass的原理和用法
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python专题第18篇文章,我们来继续聊聊Python当中的元类. 在上上篇文章当中我们介绍了type元类的用法,在上一篇文章当中我 ...
随机推荐
- [图形学] Chp18 OpenGL表面纹理函数
以2D表面为例展示纹理贴图,用opengl设置一个2D纹理,颜色存储在32*32*3的数组中,对应的纹理坐标为0<=s, t<=1.0. 画出几个正方形表面,分别以GL_CLAMP(纹理坐 ...
- SQL 中 decode()函数
文本转自 FreeSpider的微博 今天看别人的SQL时看这里面还有decode()函数,以前从来没接触到,上网查了一下,还挺好用的一个函数,写下来希望对朋友们有帮助哈! decode()函数简介: ...
- Mybatis-多对多
先说一下需求: 在页面上显示数据库中的所有图书,显示图书的同时,显示出该图书所属的类别(这里一本书可能同时属于多个类别) 测试环境:MySQL.MyEclipse 创建表: 笔者这里使用 中间表 连接 ...
- 2.1 insertion sort 《算法导论》答案
2.1 insertion sort <算法导论>答案 答案索引帖 2.1-1 Using Figure 2.2 as a model, illustrate the operation ...
- Android - 使用Volley请求网络数据
Android - 使用Volley请求网络数据 Android L : Android Studio 14 个人使用volley的小记,简述使用方法,不涉及volley源码 准备工作 导入Volle ...
- FreeRTOS——内存管理
1. 标准malloc() 和 free() 库函数的缺陷: 1)在小型的嵌入式系统中,可能不可用. 2)具体实现相对较大,占用较多宝贵的代码空间. 3)通常不具备线程安全性. 4)具有不确定性,每次 ...
- Docker for windows10 配置镜像加速
windows玩docker的时候,经常碰到这种问题: Error response from daemon: Get https://registry-1.docker.io/v2/: net/ht ...
- GetConsoleScreenBufferInfo 函数--获取控制台屏幕缓冲区信息
GetConsoleScreenBufferInfo函数 来源:https://msdn.microsoft.com/en-us/library/ms683171(v=vs.85).aspx 作用 获 ...
- 一个爬取Bing每日壁纸的python脚本
1. 背景 Bing搜索每天的背景图片有些比较适合做桌面,但是有的提供下载有的不提供下载.每天去点击下载又不太方便,所以第一次学习了一下python爬虫怎么写,写的很简单. 2. 相关技术 2.1 P ...
- jQuery请求后台接口
function test() { console.log("请求准备发送"); $.ajax({ type : "POST", url : "/ap ...