编译系统1:预处理

对于编译链接过程一直了解得不是太透彻,今天学习学习,从最经典的Hello World开始:

#include <stdio.h>

int main(){

    printf(“Hello World\n”);

    return 0;

}

在linux环境下,通过gcc hello.c可生成a.out,然后运行./a.out即可打印Hello World,其实通过源文件生成可执行二进制程序经历了预处理,编译,汇编,链接4个阶段:

hello.c=>预处理器cpp=>hello.i=>编译器cc1=>hello.s=>汇编器as=>hello.o=>链接器ld=>a.out

预处理阶段:

预处理器cpp根据以字符#开头的命令,会修改原始的C程序,根据命令告诉预处理器读取系统头文件的内容(上面这个程序里stdio.h),并把它直接插入到程序文本当中,结果就得到了另一个C程序,而且是以.i作为文件扩展名

操作如下:

lihui@LastWish ~ $ gcc -E hello.c -o hello.i

或者

lihui@LastWish ~ $ cpp hello.c > hello.i

lihui@LastWish ~ $ cat hello.i | wc -l
1250

可见的确hello.i新添加了很多的内容,有兴趣的可以自己实验一把,看看.i的内容

总结起来,预编译主要有以下处理:

1:将所有的#define删除,并且展开所有的宏定义

2:处理所有的条件预编译指令,比如#if,#ifdef,#elif,#else,#endif等

3:处理#include预编译指令,将包含的文件插入到该预编译指令的位置,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件

4:删除所有的注释//和/* */

5:添加行号和文件名标识,比如#2 “hello.c” 2,以便编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或者警告时能够显示行号

6:保留所有的#pragma编译器指令,因为编译器需要使用它们

经过了预编译后的.i文件不再包含任何宏定义,因为所有的宏定义都可以被展开,并且包含的文件也被插入到了.i文件当中,所以当我们无法判断宏定义是否正确或者头文件包含是否正确时,可以查看预编译后的文件来确定问题

发表回复