背景:
如下图所示,我们通过gcc命令生成可执行程序。以下两种写法的最终效果都一样,唯一不同的是第二种写法指定了输出的可执行程序的名字,而第一种写法经gcc编译后其生成的可执行程序的名字默认是a.out
。下面我们看看gcc hello.c -o hello
此命令背后做了哪些工作。
事实上,上述过程基本可以分为4个步骤,分别是:
预处理(Prepressing)编译(Compilation)汇编(Assembly)链接(Linking)
1、预编译
(1) 作用:
a. 将所有的 “#define” 删除,并展开所有的宏定义。
b. 处理所有的条件预编译指令,比如:" #if #ifdef #elif #else #endif "。
c. 处理所有的 “#include” 预编译指令。
d. 删除所有的注释 “//” 、 “/* */”。
e. 添加行号和文件名标识,以便编译时产生的行号信息以及用于编译错误或警告时能够显示行号。
f. 保留所有的 “#pragma” 编译器指令。
(2) 命令:
# 以下命令等价,用来生成.i文件gcc -E hello.c -o hello.igcc -E hello.c > hello.i
(3) 结果:
使用gcc参数-E表示只进行预编译,预编译生成的
.i
文件不包含任何宏定义,因为所有的宏已经被展开,所以我们可以使用-E选项调试宏函数。
2、编译
(1) 作用 :
编译过程就是把预处理完成的文件进行一系列的词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件。这个过程往往是我们所说的整个程序构建的核心部分,也是最复杂的部分之一。
(2) 命令:
# 以下命令等价,用来生成.s文件gcc -S hello.i -o hello.s gcc -S hello.c -o hello.sgcc -S hello.c > hello.sgcc -S hello.c
(3) 结果:
使用gcc 的 -S 参数进行编译,得到汇编文件
.s
。
3、汇编
(1) 作用:
汇编是 汇编器as 把汇编代码转变成中间目标文件。
(2) 命令:
# 以下命令等价,用来生成.o文件gcc -c hello.s -o hello.o gcc -c hello.c -o hello.ogcc -c hello.c
(3) 结果:
可以得到中间目标文件
*.o
。
4、链接(生成可执行程序)
(1) 作用:
链接器 ld:负责将程序的目标文件与所需的所有附加的目标文件连接起来,
附加的目标文件包括静态连接库和动态连接库。
链接是链接器ld把中间目标文件和相应的库一起链接成为可执行文件。
(2) 命令:
gcc hello.o -o app
执行此命令,将生成一个名为app的可执行文件。
当然此处,我们可以直接写一个Makefile,然后一键生成,观看情况,Makefile内容如下:
app:gcc -E hello.c -o hello.igcc -S hello.c -o hello.sgcc -c hello.c -o hello.ogcc hello.o -o resclean:rm hello.i hello.s hello.o res
可以 通过vim来观察各个阶段的的文件信息。