Linux:预处理、编译、汇编、链接
从直观角度而言,编译器就是将高级语言翻译成机器语言(二进制)的一个工具。
C语言之经典“Hello World"可会写?入关第一天的挑战,相信大家已经可以信手拈来,那么你已经和我一样离成功不远了。
#include<stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
Linux下,我们可以用gcc一步就可生成可执行文件,事实上它是分为以下四部处理的。
1.预处理:生成.i文件(-E表示只进行预编译)
gcc -E main.c -o main.i
2.编译:生成.s文件
gcc -S main.i -o main.s
3.汇编:生成.o文件,即可重定位的二进制文件
gcc -C main.s -o main.i
4.链接:生成可执行的elf格式文件
gcc main.c -o main
那么我们再从代码角度分析一下它的处理过程:
预处理阶段
1.删除所有"#define",并展开宏定义。
2.处理所有预编译指令,如"#if" 、"#endif"等。
3.处理"#include"指令,过程是递归处理的。
4.删除所有的注释。
5.添加行号和文件标识。
6.保留所有的"#pragma",编译器要使用。
经过预编译后的.i文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经被插入到.i文件中。所以我们发现宏定义和头文件出错时,它就发生在预编译阶段。
编译阶段
1.词法分析
2.语法分析
3.语义分析
4.优化代码
5.汇总符号
汇编阶段
1.将汇编指令翻译成可重定位的二进制目标文件
2.生成符号表
3.生成各个段的section
链接阶段
1.合并各个section,调整段的大小以及起始位置
2.符号解析
3.分配地址和空间,找到符号对应的虚拟内存地址
4.符号重定位
程序链接完成后,生成的可执行文件都会有4G的虚拟地址空间。
虚拟地址空间
蓝色区域表示3G用户空间,绿色区域表示1G内核空间。
所有的进程都拥有属于自己的用户空间,但却共享一个内核空间。
可以阅读《程序员的自我修养》和《编译原理》了解更详细的编译原理知识。
下一篇: Linux详解及面试题