其实学过编程的人应该都知道编译这么个东西,英文叫做compiling,看上去也就是gcc 某个源文件,然后就生成了一个可执行的文件a.out。不过能够深入理解GCC具体的编译过程却是不少人忽视了的,下面我就来谈谈编译的4个阶段。
GCC 编译可以分为4个阶段:预处理(pre-processing)、编译(compiling)、汇编(assembling)、链接(linking)。
一、预处理阶段 gcc -E hello.c -o hello.i
GCC预处理阶段第一个主要完成的工作是头文件展开,例如一开始#include <stdio.h>,那么预处理阶段就会把这个stdio.h文件加载到你的.c中去,这边还有一个要注意的是#include "stdio.h" 和 #include <stdio.h> 的区别,<>这个尖括号符号代表的意思是让程序在预处理阶段加载文件的时候到预定义的缺省路径下寻找该文件,那缺省路径是什么呢?在LINUX下可以使用 echo | gcc -E -v - 来显示出include包含的路径
就是上图所显示的三个路径,如果都没有找到,那么gcc就会报错。而""包含的文件首先在源文件所在目录下寻找该头文件,如果没有找到则在命令行-I指定的目录中寻找,如果还没找到,则到预处理缺省路径中寻找,如果还没找到则报错;第二个完成的工作就是宏定义和条件编译处理,ANSI标准可用的预处理宏定义和条件编译命令一共有以下这些个:
#define,#error#include,#if,#else,#elif,#endif,#ifdef,#ifndef,#undef,#line,#pragma等。非常明显,所有预处理命令均以符号#开头。
它们的具体用法不是这篇博文该阐述的,有兴趣的可以搜寻其他博主的文章学习。特别注意#pragma用法很多。
二、编译阶段 gcc -S hello.i -o hello.s
gcc在编译阶段进行代码的规范性检查以及代码是否有语法错误,如果没有就生成.s汇编文件。关于汇编本人表示也不太会,如果有兴趣的读者可以自己搜索其他相关文章学习汇编语言。
三、汇编阶段 gcc -c hello.s -o hello.o
将编译生成的汇编文件转换成二进制代码文件.o文件。
四、链接阶段 gcc hello.c -o hello.exe
一旦完成这个阶段就会直接生成一个可执行文件。那么链接阶段到底干了什么呢?
我们都知道预处理的时候虽然加载了stdio.h,但是这个头文件里面全都是一些声明,并没有具体的代码实现,例如调用了printf函数的时候,stdio.h里面没有实现代码,所以链接阶段主要的工作就是链接所需要的函数库。printf这个函数是在libc.so.6这个库里面,所以链接阶段gcc编译器会链接到这个库,所以就实现了这个函数的调用。
函数库又可以分为静态库和动态库,不过这边是浅谈4个过程,所以就不再详细讲解这方面知识点,我会在以后的博文中继续理解这两个库是怎么完成的,以及他们各自的特点。