1、预处理(Prepressing)、编译(Compilation)、汇编(Assermbly)、链接(Linking)
2、预编译器cpp 将hello.c以及相关头文件编译成一个.i文件,gcc -E hello.c -o hello.i 或者 cpp hello.c > hello.i;预编译过程主要处理的事情(1)删除"#define" 展开宏(2)处理条件编译,"#if","#ifdef"(3)处理"#include",将被包含的文件递归插入该预编译指令的位置(4)删除所有注释"//""/**/"(5)添加行号和文件名标识,#2,"hello.c" 以便编译时可以产生调试用的行号以及编译错误或警告显示的行号(5)保留所有的#pragma编译器指令。
3、编译过程就是把预处理完的文件进行一系列的词法分析,语法分析,语义分析以及优化后生成相应的汇编代码文件,编译器cc1 将hello.i 编译成.s文件 gcc -S hello.i -o hello.s 或者 /usr/lib/gcc/i486-linux-gnu/4.1/cc1 hello.c
4、汇编器as是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。as hello.s -o hello.o 或者gcc -c hello.s -o hello.o
5、链接器ld
6、词法分析,运用一种类似有限状态机的算法可以轻松的将源代码的字符序列分割成一系列的记号。关键字,标识符,字面量,特殊符号 lex程序可以实现词法扫描。
7、语法分析,将对扫描器产生的记号进行语法分析,从而产生语法树。整个过程采用了上下文无关语法,简单来说,由语法分析器生成的语法分析器生成的语法书就是以表达式为节点的树。yacc(Yet Another Compiler Compiler)可以实现语法分析。
8、语义分析,编译器所分析的语义是静态语义,在编译期可以确定的语义,与之对应的动态语义只有在运行期才能确定,静态语义通常包括生命和类型的匹配,类型的转换。动态语义一般指在运行期出现的语义相关的问题,比如将0作为除数。
9、中间语言生成,源代码优化器往往将整个语法树转换成中间代码,一般跟目标机器和运行时环境是无关的。比较常见的又三地址码。中间代码使得编译器可以被分为前端和后端,前端负责产生机器无关的中间代码,后端负责将中间代码转换成目标机器代码。
10、目标代码生成与优化,编辑器后端包括代码生成器和目标代码优化器。
11、重新计算各个目标的地址过程被叫做重定位(Relocation),汇编语言使用接近人类的各种符号和记忆来帮助记忆,汇编语言也可以使用符号来标记位置。
12、人们开始将代码按照功能或者性质划分,分别形成不同的功能模块,不同的模块之间按照层次结构或者其他结构来组织。
13、静态语言C/C++模块之间通信方式最常见的两种方式,一种是模块之间的函数调用,另外一种是模块之间的变量访问。都可以归结为,模块间符号的引用。
14、组装各个模块之间的过程就是链接。主要包括了地址和空间分配,符号决议,重定位等步骤。
15、符号决议更倾向于静态链接,而绑定更倾向于动态链接。
16、我们在程序模块main.c中使用外一个模块func.c中的函数foo().我们在main.c模块中的每一处调用foo的时候都必须确切知道foo这个函数的地址,但是由于每个模块都是单独编译的,在编译器编译main.c的时候并不知道foo函数的地址,所以它暂时把这些调用foo的指令的目标地址搁置,等待最后链接的时候由链接器去将这些指令的目标地址修正。如果没有连接器,需要我们手工吧每个调用foo的指令进行修正,则填入正确的foo函数地址。当func.c模块被重新编译,foo函数的地址有可能改变时,那么我们在main.c中所有使用到foo的地址的指令将要全部重新调整。这些繁琐的工作将成为程序员的噩梦。使用连接器,你可以直接引用其他模块的函数和全局变量而无须知道他们的地址,因为链接器在链接的时候,会根据你所引用的符号foo,自动去相应的func.c模块查找foo的地址,然后将main.c模块中所有引用到foo的指令重新修正,让他们的目标地址为真正的foo函数地址。这就是静态链接的最基本的过程和作用。
2、预编译器cpp 将hello.c以及相关头文件编译成一个.i文件,gcc -E hello.c -o hello.i 或者 cpp hello.c > hello.i;预编译过程主要处理的事情(1)删除"#define" 展开宏(2)处理条件编译,"#if","#ifdef"(3)处理"#include",将被包含的文件递归插入该预编译指令的位置(4)删除所有注释"//""/**/"(5)添加行号和文件名标识,#2,"hello.c" 以便编译时可以产生调试用的行号以及编译错误或警告显示的行号(5)保留所有的#pragma编译器指令。
3、编译过程就是把预处理完的文件进行一系列的词法分析,语法分析,语义分析以及优化后生成相应的汇编代码文件,编译器cc1 将hello.i 编译成.s文件 gcc -S hello.i -o hello.s 或者 /usr/lib/gcc/i486-linux-gnu/4.1/cc1 hello.c
4、汇编器as是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。as hello.s -o hello.o 或者gcc -c hello.s -o hello.o
5、链接器ld
6、词法分析,运用一种类似有限状态机的算法可以轻松的将源代码的字符序列分割成一系列的记号。关键字,标识符,字面量,特殊符号 lex程序可以实现词法扫描。
7、语法分析,将对扫描器产生的记号进行语法分析,从而产生语法树。整个过程采用了上下文无关语法,简单来说,由语法分析器生成的语法分析器生成的语法书就是以表达式为节点的树。yacc(Yet Another Compiler Compiler)可以实现语法分析。
8、语义分析,编译器所分析的语义是静态语义,在编译期可以确定的语义,与之对应的动态语义只有在运行期才能确定,静态语义通常包括生命和类型的匹配,类型的转换。动态语义一般指在运行期出现的语义相关的问题,比如将0作为除数。
9、中间语言生成,源代码优化器往往将整个语法树转换成中间代码,一般跟目标机器和运行时环境是无关的。比较常见的又三地址码。中间代码使得编译器可以被分为前端和后端,前端负责产生机器无关的中间代码,后端负责将中间代码转换成目标机器代码。
10、目标代码生成与优化,编辑器后端包括代码生成器和目标代码优化器。
11、重新计算各个目标的地址过程被叫做重定位(Relocation),汇编语言使用接近人类的各种符号和记忆来帮助记忆,汇编语言也可以使用符号来标记位置。
12、人们开始将代码按照功能或者性质划分,分别形成不同的功能模块,不同的模块之间按照层次结构或者其他结构来组织。
13、静态语言C/C++模块之间通信方式最常见的两种方式,一种是模块之间的函数调用,另外一种是模块之间的变量访问。都可以归结为,模块间符号的引用。
14、组装各个模块之间的过程就是链接。主要包括了地址和空间分配,符号决议,重定位等步骤。
15、符号决议更倾向于静态链接,而绑定更倾向于动态链接。
16、我们在程序模块main.c中使用外一个模块func.c中的函数foo().我们在main.c模块中的每一处调用foo的时候都必须确切知道foo这个函数的地址,但是由于每个模块都是单独编译的,在编译器编译main.c的时候并不知道foo函数的地址,所以它暂时把这些调用foo的指令的目标地址搁置,等待最后链接的时候由链接器去将这些指令的目标地址修正。如果没有连接器,需要我们手工吧每个调用foo的指令进行修正,则填入正确的foo函数地址。当func.c模块被重新编译,foo函数的地址有可能改变时,那么我们在main.c中所有使用到foo的地址的指令将要全部重新调整。这些繁琐的工作将成为程序员的噩梦。使用连接器,你可以直接引用其他模块的函数和全局变量而无须知道他们的地址,因为链接器在链接的时候,会根据你所引用的符号foo,自动去相应的func.c模块查找foo的地址,然后将main.c模块中所有引用到foo的指令重新修正,让他们的目标地址为真正的foo函数地址。这就是静态链接的最基本的过程和作用。
17、链接时地址修正的过程叫做重定位,每个要被修正的地方叫做一个重定位入口。重定位所做的就是给程序中每个这样的绝对地址引用的位置"打补丁",使他们指向正确的地址。
#pramgma???
有限状态机???