经常看到文件编译,编译到底是干嘛?
1、什么是编译?
对于同一个语句,有高级语言、低级语言、机器语言的方式来表示。
C语言:
a=b+1;
汇编语言:
mov -0xc(%ebp),%eax add $0x1,%eax mov %eax,-0x8(%ebp)
机器语言:
8b 45 f4 83 c0 01 89 45 f8
因为机器是只能做数字计算的,能够让机器去运算的,数字的语言就是机器语言。除此之外的所有计算机语言都是非机器语言。为了让机器可理解,相对于机器语言的高级语言都需要一个转换,从高级、机器不可理解,转换为机器可理解的机器语言,这样的一个转换过程就叫做 编译(Compile),由 编译器(Compiler)来完成。
由 C 转换为 汇编语言 这一过程是由 汇编器(Assembler)来执行的。
C 和 汇编语言 转换为 机器语言 都是由 编译器 来完成的。
2、将C语言编译为机器语言的整个过程
在Linux下,使用gcc编译器:
1、预编译hello.c文件:
gcc -E -o hello.i hello.c
执行成功后,会生成一个新的hello.i 的文件,可以用编辑器(Vim)查看它的内容,这个文件就是经过预编译后的内容。
预编译又称为预处理,是做些代码文本的替换工作。预编译可以处理#开头的指令,比如拷贝#include包含的文件代码,#define的宏定义的替换,条件编译等。
预处理的过程主要处理包括以下过程:
将所有的#define删除,并且展开所有的宏定义;
处理所有的条件预编译指令,比如 #if #ifdef #elif #else #endif等;
处理#include预编译指令,将被包含的文件插入到该预编译指令的位置;
删除所有注释 "//" 和 "/**/";
添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号;
保留所有的#pragma编译器指令,因为编译器需要使用它们;
通常使用以下命令来处理预处理:
gcc -E hello.c -o hello.i
参数-E表示只进行预处理,或者也可以使用以下指令完成预处理过程:
cpp hello.c > hello.i /* cpp - The C Preprocessor*/
直接cat hello.i 你就可以看到预处理后的代码。
2、纯粹的进行编译:
gcc -S -o hello.s hello.i
把.i文件写为hello.c也行,就是跳过手动预编译,直接完成预编译和编译两个过程。这时会得到一个hello.s文件,打开看一下,里面是编译好的适用于当前体系结构的汇编代码。
编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码。
gcc -S hello.i -o hello.s
或者:
/usr/lib/gcc/i486-linux-gnu/4.4/cc1 hello.c
注:现在版本的gcc把预处理和编译两个步骤合成一个步骤,用cc1工具来完成。gcc其实是后台程序的一些包装,根据不同参数去调用其他的实际处理程序,比如:预编译编译程序cc1、汇编器as、连接器ld。可以看到编译后的汇编代码(hello.s)如下:
.file "hello.c" .section .rodata .LC0: .string "Hello, world." .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp andl $-16, %esp subl $16, %esp movl $.LC0, (%esp) call puts movl $0, %eax leave ret .size main, .-main .ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3" .section .note.GNU-stack,"",@progbits
3、把汇编代码进行汇编可执行:
gcc -c -o hello.o hello.s
把.s文件换成.c也行,就是自动完成预编译、编译和汇编三个过程。现在得到一个hello.o文件,这是一个二进制文件,但不是最后的可执行二进制文件,因为它还缺少最后一步链接处理。
汇编器是将汇编代码转变成机器可以执行的命令,每一个汇编语句几乎都对应一条机器指令。汇编相对于编译过程比较简单,根据汇编指令和机器指令的对照表一一翻译即可。
gcc -c hello.c -o hello.o
或者:
as hello.s -o hello.co
由于hello.o的内容为机器码,不能以普通文本形式查看(vi打开看到的是乱码)。
4、链接
最后对.o文件进行链接,这里就一个.o文件所以简单,经常是需要有多个.o文件需要链接。链接执行:
gcc -o hello hello.o
如果把最后的.o文件写成.c,那就和最开始我们用hello.c编译时示范的那样了。实际上那样是完成了预编译、编译、汇编和链接一连串的过程。
通过调用链接器ld来链接程序运行需要的一大堆目标文件,以及所依赖的其它库文件,最后生成可执行文件。
ld -static crt1.o crti.o crtbeginT.o hello.o -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o (省略了文件的路径名)