使用gcc进行编译c语言文件很简单:
gabriel@gabriel-laptop:~$ gcc hello.c -o hello
但是事实上,这个过程可分为4步,分别是预处理(Prepressing),编译(Compilation),汇编(Assembly)和链接(Linking),下面分别简述以下这四个过程,并辅以实例给大家以感性认识。
预编译
预编译过程主要是处理源文件中#开头的预编译指令,主要规则如下
下面就只预编译一个简单的程序看看,这里只使用#include引入自己定义的头文件。
头文件hello.h如下:
int headeri = 10;
主文件hello.c如下:
#include "hello.h" #define HELLO 3 int main() { #if HELLO int a[HELLO]; #else int a[10]; #endif int b = 2, c = 3, sum; sum = b + c; /* sum */ return 0; }
.c文件经过预编译之后将变成.i文件 .cpp文件经过预编译之后将会变为.ii文件
预编译可使用如下指令,其中gcc中-E选项代表只进行预编译
gabriel@gabriel-laptop:~$ gcc -E hello.c -o hello.i
或
gabriel@gabriel-laptop:~$ cpp hello.c > hello.i
查看文件hello.i
# 1 "hello.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "hello.c" # 1 "hello.h" 1 int headeri = 10; # 2 "hello.c" 2 int main() { int a[3]; int b = 2, c = 3, sum; sum = b + c; return 0; }
这样之后预编译的过程就算结束了
编译
编译的过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编文件代码(属于编译原理的内容)
编译之后将生成.s文件,即汇编代码的文件,继续使用上一步预编译完成的hello.i文件进行编译,使用如下命令,其中-S选项代表进行到编译
gabriel@gabriel-laptop:~$ gcc -S hello.i -o hello.s
同样,也可以直接从源文件进行预处理和编译
gabriel@gabriel-laptop:~$ gcc -S hello.c -o hello.s
另外,gcc专门有一个程序是完成以上编译加汇编两个步骤的,是位于/usr/lib/gcc/i486-linux-gnu/4.4/下的cc1(对于C++程序来说是cc1plus)
gabriel@gabriel-laptop:~$ /usr/lib/gcc/i486-linux-gnu/4.4/cc1 hello.c
可以看一下汇编出来的代码hello.s
.file "hello.c" .globl headeri .data .align 4 .type headeri, @object .size headeri, 4 headeri: .long 10 .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $32, %esp movl $2, -12(%ebp) movl $3, -8(%ebp) movl -8(%ebp), %eax movl -12(%ebp), %edx leal (%edx,%eax), %eax movl %eax, -4(%ebp) movl $0, %eax leave ret .size main, .-main .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits
汇编代码就不过多解释了,呵呵
汇编
汇编器是将汇编代码转变为机器可以执行的指令,也就是机器代码,由于每一个汇编语句几乎都对应一条机器指令,所以相对编译来说比较简单
汇编之后生成的是目标文件.o,这已经是机器代码的文件了。继续使用上一步编译之后的hello.s文件进行汇编,使用如下指令,其中-c代表汇编
gabriel@gabriel-laptop:~$ gcc -c hello.s -o hello.o
或者直接使用汇编器as
gabriel@gabriel-laptop:~$ as hello.s -o hello.o
若要从源文件直接获得目标文件,则可以
gabriel@gabriel-laptop:~$ gcc -c hello.c -o hello.o
链接
最后是链接,链接是一个非常复杂的过程,虽然目标文件已经是机器代码了,但是仍要通过各种链接才能最终变成可执行文件,linux下的链接器是ld。本书前半部分大部分篇幅都会讨论链接,所以具体内容后文将给出。
总结一下
一个c语言文件从源码文件编译链接成为可执行文件的整个过程如下所示
.c -> 预编译 -> .i -> 编译(cc1) -> .s -> 汇编(as) -> .o -> 链接(ld) -> 可执行文件