从源文件到可以行文件的过程:
预处理——编译——汇编——链接
第一步
预编译:
$gcc -E hello.c -o hello.i
或者
$cpp hello.c > hello.i
注:‘-E’选项表示只进行预编译;cpp是预编译器
预编译主要处理一‘#’开头的预编译指令:
将所有的"#define"删除,并且展开所有的宏定义
处理所有条件预编译指令
处理"#include",将包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行
删除所有注释
添加行号和文件名标识,以便于调试和编译产生的错误和警告
保留所有"#pragma"指令,编译器需要使用它们
编译:
$gcc -S hello.i -o hello.s
也可以将编译和预编译合并,通过一个gcc提供的程序ccl来完成:
$/usr/lib/gcc/usr/lib/gcc/i686-pc-cygwin/ccl hello.c
或者:
$gcc -S hello.c -o hello.s
实际上gcc这个命令只是一些列后台程序的包装,它会根据不同的参数要求去调用预编译程序ccl、汇编器as、连接器ld。
汇编:是将汇编代码转换为机器码的过程
这样来完成汇编:
$as hello.s -o hello.o
或者:
$gcc -c hello.s -o hello.o
或者:从.c文件开始
$gcc -c hello.c -o hello.o
链接:将.o文件变为可执行文件的过程(这是一个复杂的过程)
需要经过下面的一些命令来完成:
ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh
-lc-end-group crtend.o crtn.o
注:到后面的时候记录一下各个参数的作用,然后在这里补充
编译器都做了什么:
编译过程一般可以分为6步:扫描、语法分析、语义分析、源代码优化、代码生成和目标代码优化。
词法分析:
运用有限状态机(Finite State Machine)可以轻松的将源代码的字符序列分割成一系列的记号。
此法分析产生的记号一般有一下几类:关键字、标识符、字面量(包含数字、字符串等)和特殊符号(如加号、等号)
语法分析:
语法分析器(Grammar Parser)将对由扫描器产生的记号进行语法分析,从而产生语法树。
由语法分析器生成的语法树就是以表达式为节点的树。
语义分析:
编译器所能分析的语义是静态语义,所谓静态语义是指在编译期间可以确定的语义,与之相对应的动态语义,就是在运行期才能确定的语义。
函数访问必须知道目标函数的地址,变量访问也必须知道目标变量的地址,所以这两种方式都可以归结为一种方式,那就是模块间符号的引用。
人们把每个源代码模块独立地编译,然后按照需要将它们组装起来,这个组装模块的过程就是“链接”
链接的主要内容就是把各个模块之间相互引用的部分处理好,使得各个模块之间能够正确衔接。
链接过程主要包括地址和空间分配、符号决议和重定位。
重定位发生在链接阶段,如果一个模块需要使用另一个模块的变量或函数,编译的时候先给这个变量或函数一个特殊的地址,链接的时候再来确定实际地址。这个 地址修正的过程也叫做重定位,每个要被修正地方叫一个重定位入口。