C/C++语言预处理,编译,汇编,链接的详细过程讲解

一、问题背景

最近博主在复习各类编程语言知识时,在类比Java解释型高级语言的运行机制时,第一时间想到了C/C++的运行机制,于是博主我查阅相关资料后对C/C++的运行机制做一个详细但易懂的总结归纳。

二、C/C++的运行机制

程序要运行起来,必须要经过四个步骤:预处理、编译、汇编和链接。

预处理
编译
汇编
链接
hello.c, hello.h
hello.i
hello.s
hello.o或hello.obj
可执行文件Windows的.exe文件或Linux的.out文件等

1.预处理

C/C++语言最常见的预处理就是将所有的“#define”删除,并且展开所有的宏定义。而预处理其实还包括:处理所有的条件编译指令,比如“#if”、处理“#include”预编译指令、删除所有的注释、添加行号和文件名标识等。

注意:GCC命令来自于Linux 系统C/C++的GCC编译器,而Windows 下常用的编译器是微软开发的 Visual C++,它被集成在 Visual Studio 中。

在这里插入图片描述
上图是CSDN博主douguailove在他的博客编译和链接的过程中使用Linux系统下的gcc -E命令把hello.c文件预处理成hello.i文件后的对比图,图左侧为hello.c文件,图右侧为hello.i文件。

预处理后的.i被称为扩展后的c源码文件。为什么还叫c源码文件呢?
因为预处理后,只是宏定义等东西不见了,但是C源码依然还在,比如main函数,各种自己写的子函数,依然存在,所以还是被称为c源码文件。打开.i文件后,我们是能够看的懂的,所以.c文件、.h文件和.i文件都是ascii文件。

2.编译

2.1编译词汇的争议性

对于C/C++这种编译性语言,我们平时编译时,不管是通过IDE图形界面,还是通过命令行,总感觉编译一下就完成了,然后就得到了针对某操作系统和某CPU的二进制可执行文件(机器指令的文件)。但是实际上在源码到可执行文件中间隐藏了四个过程,这四个过程被操作系统默默的处理了。

编译四个过程:预处理、编译、汇编、链接

四个过程中的“编译”,特指其中的某个过程,这四个过程合在一起,我们也统称为编译。所以编译二字到底指的是第二个过程,还是全部过程的统称,这个就要看说话的“语境”了。其实统称的编译,完整的称法应该叫编译链接,只是简称为编译而已。

如果这四个过程是一次性编译完成的,这个四个过程分别会产生相应的文件,只不过中间产生的文件都是过渡性的临时文件,使用完成后就会被删除。

2.2编译的定义

编译会将源代码由文本形式转换成机器语言,编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后生成相应的汇编代码文件。

编译后的.s也是ascii码文件。因为汇编也是人能看懂的文字编码形式,所以.s汇编文件也是ASCII码文件。

3.汇编

汇编过程调用汇编器AS来完成,是用于将汇编代码转换成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。(非底层的程序员不需要考虑)

汇编后的.o文件是纯二进制文件。因为.o中放的是纯二进制的机器指令,所以我们打开后看不懂。
ASCII的源码被汇编为能被CPU执行的机器指令,.o文件中放的就是机器指令。但是.o文件还无法运行,需要链接后才能运行。

4.链接

链接是将所有的.o文件和库(动态库、静态库)链接在一起,得到可以运行的可执行文件(Windows的.exe文件或Linux的.out文件)等。它的工作就是把一些指令对其他符号地址的引用加以修正。链接过程主要包括了地址和空间分配、符号决议和重定向。

最基本的链接叫做静态链接,就是将每个模块的源代码文件编译、汇编成目标文件(Linux:.o 文件;Windows:.obj文件),然后将目标文件和库一起链接形成最后的可执行文件(.exe或.out等)。库其实就是一组目标文件的包,就是一些最常用的代码变异成目标文件后打包存放。最常见的库就是运行时库,它是支持程序运行的基本函数的集合。

5.可执行文件(Windows的.exe文件或Linux的.out文件)

你是否曾疑惑“a.out”这个名字是怎样确定的?把所有的输出文件都缺省的使用同一个名字a.out可能会带来不便,可能会忘了它来自哪一个源文件,对任何文件进行下一次编译时都有可能覆盖它。

a.out是“assembler output”(汇编程序输出)的缩写形式。这里有一个问题:它不是汇编程序输出,而是链接器输出。“汇编程序输出”这个名字的产生纯属历史原因。在早期的语言中并不存在链接器,程序是这样创建的:先把所有源文件连接在一起,然后进行汇编,汇编产生的汇编程序输出保存在a.out中。即使最后有了链接器之后,最后一个环节的输出文件依然沿用了这个命名习惯。

UNIX中的可执行文件是以一种特殊的方式加上标签,这样系统就能确认它们的特殊属性。为重要的数据定义标签,用独特的数字唯一的标识该数据是一种普遍采用的编程技巧。可执行文件用文件的第一个字节来标注,文件以十六进制数7F开头,紧跟在后面的第二至第四个字节为“ELF”(Executable and Linking Format)可执行文件和链接格式。

参考文献:
[1]C语言编译和链接详解(通俗易懂,深入本质)
[2]c语言编译过程详解,预处理,编译,汇编,链接(干货满满)
[3]了解“预编译、编译、汇编、链接”这四个过程对你有很大帮助
[4]编译和链接的过程
[5]理解a.out
[6]预处理、编译、汇编、链接、启动代码、相关command

发布了120 篇原创文章 · 获赞 141 · 访问量 28万+

猜你喜欢

转载自blog.csdn.net/wq6ylg08/article/details/104349728