编译的过程

来源:互联网 发布:健身房健身计划软件 编辑:程序博客网 时间:2024/05/19 21:41

这篇博客主要讲解编译的过程被隐藏的步骤,只讲解主要的内容,想要更详细的了解,还需要去看关于链接,装载的书籍才行。

我们回顾下平时在控制台用gcc编译源代码时的情景。假如有一份非常简单的C源代码,叫helloworld.c
如下:

int main{   int a = 1;}

使用gcc来编译,使用如下命令:

gcc helloworld.c -o hello

然后就在linux平台下生成了一个可执行文件hello。这么这中间,编译器到底做了哪些步骤呢?大致可以分为4个步骤:预处理,编译,汇编,链接。

预处理

预处理相信大家都知道,我们定义的宏,#include指令等等,都在预处理阶段被处理。具体规则是:

将所有的#define删除,并展开所有的宏定义
处理所有条件预编译指令:比如 #if,#ifdef,#elif,#else,#endif
处理#include预编译指令,将被包含的文件插入到该预编译指令的位置
删除所有注释
添加行号和文件号
保留#pragma编译器指令

我们现在来举例子说明上面的规则到底是什么意思。我们现在已经有了一个C文件叫helloworld.c就是前面提到的,现在我们引用另外一个叫fun.c的文件,这个文件也很简单,如下:

int myfun(){    return 1;}

注意到,我是故意空了一部分空白才写函数的,myfun在fun.c的第5行被声明。然后我们在helloworld.c里添加#include “fun.c”

#include "fun.c"int main(){    int a = 1;}

好的,现在我们使用gcc -E helloworld.c -o helloworld.i命令,之后我们得到了一个heeloworld.i文件,打开这个文件可以看到:

# 1 "helloworld.c"# 1 "<built-in>"# 1 "<command-line>"# 1 "/usr/include/stdc-predef.h" 1 3 4# 1 "<command-line>" 2# 1 "helloworld.c"# 1 "fun.c" 1int myfun(){    return 1;}# 9 "helloworld.c" 2int main(){    int a = 1;}

简单的分析下这个文件,# 1 “fun.c”1 这一行和# 1 “helloworld.c”这一行中间有7个空行,正好是helloworld.c文件里#include “fun.c”这句话正好是在第8行写的。而# 1 “fun.c” 1和int myfun()这行中间有4个空行,而 int myfun()也是正好定义在fun.c的第5行。另外,你也可以试试#include

编译

好了,预处理结束了,我们得到了helloworld.i文件,之后的步骤是什么呢?之后开始编译了,我们把预处理完的文件进行词法分析,语法分析,语义分析以及优化后生成了相应的汇编代码文件,我们来看一看这个过程。
使用gcc -S helloworld.c -o helloworld.s
我们查看一下helloworld.s文件

    .file   "helloworld.c"    .text    .globl  myfun    .type   myfun, @functionmyfun:.LFB0:    .cfi_startproc    pushq   %rbp    .cfi_def_cfa_offset 16    .cfi_offset 6, -16    movq    %rsp, %rbp    .cfi_def_cfa_register 6    movl    $1, %eax    popq    %rbp    .cfi_def_cfa 7, 8    ret    .cfi_endproc.LFE0:    .size   myfun, .-myfun    .globl  main    .type   main, @functionmain:.LFB1:    .cfi_startproc    pushq   %rbp    .cfi_def_cfa_offset 16    .cfi_offset 6, -16    movq    %rsp, %rbp    .cfi_def_cfa_register 6    movl    $1, -4(%rbp)    movl    $0, %eax    popq    %rbp    .cfi_def_cfa 7, 8    ret    .cfi_endproc.LFE1:    .size   main, .-main    .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609"    .section    .note.GNU-stack,"",@progbits

汇编

我们现在有了汇编代码,使用汇编器,可以将汇编代码转换成机器可以执行的指令,每一个汇编语句几乎都会对应一条机器指令。我们现在就来实现这一过程,值得一提的是,.s的汇编文件是文本文件,而.o的目标文件是二进制文件。

我们使用gcc -c helloworld.s -o hello1.o命令来进行汇编,就能得到helloworld.o这个目标文件了,后面的博客我还会写如何查看这个目标文件,这个目标文件里都有什么,所以现在就暂时了解这么多把。hello1是我随便起的名字,没什么特别的含义,名字可以自己随便取的。

链接

有了目标文件,比如说a.o, b.o,我们可以将它们链接成一个可以运行的程序,目前a.o和b.o还不能运行,因为a.o里引用的别的函数啊,变量之类的地址还没有被确定(一般没确定的时候就先写为0)。用连接器ld来连接。链接以后也会继续开博客讲,所以目前也就写到这里。大概就知道四个步骤,以及这四个步骤都干了什么就行了。

原创粉丝点击