编译过程简介
1. 初始编译器
广义的编译器
你可能不知道的事…

2. 预编译:指令示例——gcc –E file.c –o file.i
(1)处理所有的注释,以空格代替
(2)将所有的#define删除,并且展开所有的宏定义
(3)处理条件编译指令#if、#ifdef、#elif、#else、#endif
(4)处理#include,展开被包含的文件
(5)保留编译器需要使用的#pragma指令
3. 编译:指令示例——gcc –S file.i –o file.s
(1)对预处理文件进行词法分析、语法分析和语义分析
①词法分析:分析关键字、标识符、立即数等是否合法
②语法分析:分析表达式是否遵循语法规则
③语义分析:在语法分析的基础上进一步分析表达式是否合法
(2)分析结束后进行代码优化生成相应的汇编代码文件
4. 汇编:指令示例——gcc –c file.s –o file.o
(1)汇编器将汇编代码转变为机器可以执行的指令
(2)每条汇编语句几乎都对应一条机器指令
【编程实例】源代码单步编译示例
//源文件
| 19-1.h文件 | 19-1.c文件 |
/*
This is a header file.
*/
char* p = "Hello World!";
int i = 0;
|
#include "19-1.h"
// Begin to define macro
#define GREETING "Hello world!"
#define INC(x) x++
// End
int main()
{
p = GREETING;
INC(i);
return 0;
}
|
//第1步:预处理——gcc -E 19-1.c -o test.i
//预编译后输出的文件test.i
# 1 "19-1.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "19-1.c" # 1 "19-1.h" 1 char* p = "Hello World!"; int i = 0; # 2 "19-1.c" 2 # 11 "19-1.c" int main() { p = "Hello world!"; i++; return 0; }
//第2步:编译——gcc -S test.i -o test.s
//编译的源文件来自预编译的输出文件(test.i),编译后输出的文件为test.s
.file "19-1.c" .globl p .section .rodata .LC0: .string "Hello World!" .data .align 4 .type p, @object .size p, 4 p: .long .LC0 .globl i .bss .align 4 .type i, @object .size i, 4 i: .zero 4 .section .rodata .LC1: .string "Hello world!" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp movl $.LC1, p movl i, %eax addl $1, %eax movl %eax, i movl $0, %eax popl %ebp ret .size main, .-main .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5" .section .note.GNU-stack,"",@progbits
//第3步:汇编——gcc -c test.s -o test.o
在第3步,会生成19-1.c文件对应的目标文件,本例中为test.o,这是一个二进制代码的文件
//第4步:链接——gcc test.o
第4步后就会生成可执行文件
5. 链接器的意义
链接器的主要作用是把各个模块之间相互引用的部分处理好,使得各个模块之间能够正确的衔接。

静态链接

在编译期间将所需的文件全部绑定在一起,形成一个可执行文件。
动态链接

并没有和真正的可执行程序绑定在一起,在内存某个地方只保留一份,需要的时候直接链接到该库文件处。
5. 小结——编译过程的四个阶段:预处理、编译、汇编和链接
(1)编译器将编译工作主要分为预处理,编译和汇编三部
①预处理:处理注释、宏以及以#开头的符号
②编译:进行词法分析、语法分析和语义分析等
③汇编:将汇编代码翻译为机器指令的目标文件
(2)链接器工作是把各个独立的模块链接为可执行程序
①静态链接在编译期完成,动态链接在运行期完成
浙公网安备 33010602011771号