您的位置:首页 > 编程语言

从C源代码到可执行文件的总体过程框架

2014-03-08 10:40 246 查看
出处:http://blog.csdn.net/gabriel1026/article/details/6321701

使用gcc进行编译c语言文件很简单:

gabriel@gabriel-laptop:~$ gcc hello.c -o hello

但是事实上,这个过程可分为4步,分别是预处理(Prepressing),编译(Compilation),汇编(Assembly)和链接(Linking),下面分别简述以下这四个过程,并辅以实例给大家以感性认识。

预编译

预编译过程主要是处理源文件中#开头的预编译指令,主要规则如下

将所有的#define删除,并且展开所有的宏定义
处理所有的条件编译指令:#if #ifdef #elif #else #endif
处理#include,将被包含的文件插入到该预编译指令的位置
删除所有的注释//和/* */
添加行号和文件名标识,便于编译时产生调试用的行号信息
保留所有的#pragma,因为编译器需要

下面就只预编译一个简单的程序看看,这里只使用#include引入自己定义的头文件。

头文件hello.h如下:

[cpp] view
plaincopy

int headeri = 10;

主文件hello.c如下:

[cpp]
view plaincopy

#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

[cpp]
view plaincopy

# 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;

}

可以看到

头文件hello.h已经被包含进来,在hello.i的第五行显示了hello.h里面的内容;
而hello.h前前后后带#加数字的行则是告诉编译器这里是源文件hello.c中的哪一行;
再来对照hello.c看,其中条件编译语句已经被删去,取而代之的是条件编译语句中编译条件成立的那一句;
另外,同样是在hello.c中的预处理#define HELLO 3在hello.c中已经不见了,并且main()函数中使用HELLO定义的数组a也被3代替;
最后,原本在第八行的注释在预编译后的文件中也不再显示了。

这样之后预编译的过程就算结束了

编译

编译的过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生产相应的汇编文件代码(属于编译原理的内容)

编译之后将生成.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

[cpp] view
plaincopy

.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) -> 可执行文件
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: