编译的过程
来源:互联网 发布:健身房健身计划软件 编辑:程序博客网 时间: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来连接。链接以后也会继续开博客讲,所以目前也就写到这里。大概就知道四个步骤,以及这四个步骤都干了什么就行了。
- JSP的编译过程
- 编译过程的理解
- PB的编译过程
- PB的编译过程
- qmeu的编译过程
- PB的编译过程
- 编译的过程
- GCC的编译过程
- Halite的编译过程
- mplayer的编译过程
- MTK的编译过程
- pppd的编译过程
- gcc的编译过程
- MTK的编译过程
- 程序的编译过程
- IAR的编译过程
- wince的编译过程
- Javac的编译过程
- 索尼新型CMOS图像传感器内置偏振元件
- find命令进阶(二):对找到的文件执行操作exec
- LC 2 pointer summary
- 数学中的特殊字符
- 算法导论程序35--动态规划(钢条切割)
- 编译的过程
- H
- 为什么Android Studio通过app打电话发短信时程序会崩溃
- effective Java读书笔记-通用程序设计
- postgresql-9.X 的 slave 端的 recovery.conf 文件
- u3d 切割texture贴图的方法
- session超时设置
- CodeChef
- Java流程控制和数组