第19课 - 编译过程简介
1. 初识编译器
我们通常所说的编译器其实是一个广义的概念,其实它里面包含了多个子模块,编译的过程由这些子模块协作完成。
编译器的工作过程
2. 编译器做了什么?
2.1 预处理
预处理由预处理器完成,预处理指令示例:gcc -E file.c -o file.i
(1)处理所有的注释,以空格代替
(2)将所有的 #define 删除,并且展开所有的宏定义
(3)处理条件编译指令 #if,#ifdef,#elif,#else,#endif
(4)处理 #include,展看被包含的文件
(5)保留编译器需要使用的 #pragma 指令
// 19-1.h
1 /* 2 This is a header file. 3 */ 4 5 char* p = "Hello World!"; 6 7 int i = 0;
// 19-1.c
1 #include "19-1.h" 2 3 // Begin to define macro 4 5 #define GREETING "Hello world!" 6 #define INC(x) x++ 7 8 // End 9 10 int main() 11 { 12 p = GREETING; 13 14 INC(i); 15 16 return 0; 17 }
gcc -E 19-1.c -o 19-1.i 的预处理结果
# 1 "19-1.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 1 "<command-line>" 2 # 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.2 编译
编译由编译器(狭义)完成,编译指令示例:gcc -S file.i -o file.s
(1)对预处理文件进行词法分析、语法分析和语义分析
- 词法分析:分析关键字、标识符、立即数等是否合法
- 语法分析:分析表达式是否遵循词法规则
- 语义分析:在语法分析的基础上进一步分析表达式是否合法
(2)分析结束后进行代码优化生成相应的汇编代码文件
gcc -S 19-1.i -o 19-1.s 的预处理结果
.file "19-1.c" .globl p .section .rodata .LC0: .string "Hello World!" .data .align 8 .type p, @object .size p, 8 p: .quad .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: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq $.LC1, p(%rip) movl i(%rip), %eax addl $1, %eax movl %eax, i(%rip) movl $0, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.4) 4.8.4" .section .note.GNU-stack,"",@progbits
2.3 汇编
汇编由汇编器完成,汇编指令示例:gcc -c file.s -o file.o file.o是二进制文件
(1)汇编器将汇编代码转变为机器的可执行指令
(2)每条汇编语句几乎都对应一条机器指令
2.4 链接
通过连接器生成最终的可执行文件,链接器具体是如何工作的,我将在下一篇文章中讲解。