探究编译与链接
选择程序员入门程序来当门神(hello.c)
#include<stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
被隐藏了的过程
我们使用gcc编译一个程序,只需使用一个命令,例如编译程序 hello.c
$gcc hello.c
会生成一个可执行文件 a.out, 运行即可
$./a.out
Hello World
然其过程可以分解成以下4个步骤:
- 预处理(prepressing)
- 编译(compilation)
- 汇编(assembly)
- 链接(linking)
预编译
预编译命令(-E表示只进行预编译):
$gcc -E hello.c -o hello.i
or
$cpp hello.c > hello.i
命令处理完成之后你会发现 hello.i 文件非常之大,你如果知晓这步干了些啥,也就不足为奇了。
本职工作主要是处理源码文件中的以“#”开始的预编译指令,如“#include”、“#define”等,规则如下:
- 去除所有的“#define”,展开所有的宏定义
- 处理所有的条件预编译指令(“#if”、“#ifdef“、“#elif”、“#else”、“#endif”)
- 递归处理”#include”指令,将被包含的文件插入到该预编译指令的位置
- 删除所有的注释(“//”、“/* */”)
- 添加文件名标识和行号,便于调试
- 保留所有的 #pragma 编译器指令,因为后面编译器需要使用它们
编译
编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件。
$gcc -S hello.i -o hello.s
上面的命令可以得到汇编输出文件hello.s。对于c语言的代码来说,这个预编译和编译的程序是cc1,对于C++来说,对应的程序是cc1plus;Object-c是cc1obj; Java是jc1。故实际上gcc这个命令只是这些后台程序的包装,根据不同的参数要求去调用预编译程序cc1、汇编器as、连接器ld。
汇编
汇编器是将汇编代码转变成机器可以执行的指令。
$as hello.s -o hello.o
or
$gcc -c hello.s -o hello.o
or
$gcc -c hello.c -o hello.o
链接
链接过程主要包括了地址和空间分配(Address and Storage Allocation)、符号决议(Symbol Resolution)和重定位(Relocation)等这些步骤。
$ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc -end-group crtend.o crtn.o
Happy Writing!