《程序员的自我修养》第二章笔记

来源:互联网 发布:淘宝店铺怎么改折扣 编辑:程序博客网 时间:2024/05/18 02:39
2.1 被隐藏的过程
源码变可执行文件的四个步骤:预处理、编译、汇编、链接。
2.1.1 预编译(Prepressing)
首先进行的是预编译,这步很容易被忽略。预编译主要处理那些源码中以# 开头的指令。如"#include" "#define" 等,规则如下:
将所有"#define" 删除,并且展开所有宏定义。
处理所有条件预编译指令,比如"#if"、"#ifdef"、"#elif"、"#else"、"#endif"。
处理"#include"预编译指令,将包含文件插入到该预编译指令的位置。注意:这个过程是递归进行的,也就是说被包含的文件可能包含其他文件。
删除所有的注释 "//" 和 "/* */" 。
添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时显示行号。
保留所有#pragma 编译器指令,因为这是编译器用的。
2.1.2 编译(Compilation)
编译的过程就是把预处理完的文件进行词法分析、语法分析、语义分析和优化,最终生成相应的汇编代码文件。
题外话:实际上gcc只是很多后台命令的包装,接收不同参数,调用不同命令。
2.1.3 汇编(Assembly)
将汇编代码文件转换成机器可以执行的机器指令。相比编译,汇编过程简单一些。对于汇编器来说,只要照句翻译即可。这时会输出目标文件。
2.1.4 链接(Linking)
这还用说么?后面几章讲的就是这。。。
2.2 编译器做了什么
分为六步:扫描(词法分析)、语法分析、语义分析、源代码优化、代码生成和目标代码优化。
2.2.1 词法分析
词法分析就是对源代码进行一次扫描,将代码分割成一系列记号。
记号有如下几类:关键字(语言的关键字)、标识符(变量或函数名字之类)、字面量(数字、字符串等)和特殊符号(加号 等号)。
这里的功能和预编译有区别,不可混淆。
2.2.2 语法分析
生成语法树,对符号进行分析。涉及到语法分析器和上下文无关语法,这些以后另外学习。
2.2.3 语义分析
语义分析是分析语句是否真正有意义。例如指针相乘,指针可以作为运算量,所以这样做语法是没有问题的。但是指针相乘没有意义,语义分析无法通过。编译器的语义分析只能是静态语义,即在编译阶段就可以确定的语义。动态语义指运行阶段才能确定的语义,这不由编译器处理。
静态语义包括 声明和类型的匹配,类型的转换。
2.2.4 中间语言生成
1.编译器对源码会有优化。
2.中间代码有多种形式,如三地址码和P-代码。这部分可以优化
3.中间代码使编译器可以分为前端和后端。前端负责产生机器无关的中间代码,后端可以将中间代码转换为目标机器代码。对于跨平台的编译器而言,只需要更换后端即可。
2.2.5 目标代码生成与优化
编译器后端主要包括:代码生成器和代码优化器。
代码生成器就是生成汇编代码,这里依赖于目标机器,不同硬件环境会有不同的字长、寄存器、整数数据类型和浮点数数据类型等。
最后目标代码优化器对目标代码(汇编)进行优化,例如选择合适的寻址方式、使用位移来代替乘法运算、删除多余的指令等。由于现在高级语言很复杂,所以在这部分编译器做的事情也很复杂。

做完上述几个工作以后,目标文件也应该生成了,这时候考虑一个问题,变量的地址如何分配。如果变量定义在当前文件中,编译器可以为其分配地址,但是如果变量是其他文件里的,这里就要引入链接器来帮忙了。

2.3 链接器年龄比编译器长
从机器码到汇编,再到后来的高级语言,计算机语言的发展是漫长的。由于机器码在纸带上打孔效率问题 和 重定位问题无法解决。人们发明了汇编,加入了符号,解决了很多问题。后来出现高级语言,编程效率的提高导致代码量的增加。链接器的作用越发突出。
2.4 模块拼装——静态链接
程序设计模块化是软件开发所追求的,因为当一个系统复杂的时候,我们不得不将一个复杂的系统逐步分割成小的系统以达到各个突破的目的。人们把各个模块分别独立编译,然后组装起来,就是链接。简单来说,链接就是处理各个文件之间的引用部分。链接过程主要包括了 地址和空间分配、符号决议和重定位。

本章总结:
预处理,解决一些包含问题,条件编译问题,宏替换问题。
编译很复杂,应该说超级复杂。不但要对高级语言进行翻译(这部分还是比较好理解的,感觉也不是很困难,这部分被叫做编译器的前端),而且要生成汇编(也被叫做编译器的后端)(两个步骤分别同时进行源码级优化和汇编代码优化)。编译器的后端需要考虑运行机器的物理硬件,跨平台的编译器只需要更换后端就可以完成跨平台。代码优化很复杂,需要有很深的硬件研究,不多废话。
汇编就相对简单,逐句翻译成机器码,这个相比编译就显得很轻松了,而且经常被人们所忽略。
链接是为了解决多文件问题的,如果你的代码完全不需要引用其他文件,那也就不需要链接过程,但是,在现代编程这是不可能的,链接过程越来越受到重视,后面笔记有很多详细叙述。