编译器到底干了什么
- 1. 什么是源文件
- 2. 什么是可执行文件
- 2.1 为什么可执行文件可以执行
- 2.2 可执行文件的内容
- 3. 编译器
- 4. 如何从源文件到可执行文件
- 4.1 链接
- 4.2 库文件
- 4.3 DLL文件
- 4.4 导入库
- 4.5 静态链接库
- 参考链接
- 扩展链接
一直以来,我一直有个疑惑:编写程序为什么需要编译器。通过搜索,我知道了编译器可以将源代码编译生成可执行文件。下面本文将介绍从源代码到可执行文件的变化过程。
1. 什么是源文件
程序员可以使用 C 语言、C++、Java 等各种编程语言编写代码,保存代码的文件就是源文件。源文件其实是一种文本文件,所以理论上任何文本编辑器都可以编写源代码,哪怕是 Windows 下的记事本工具也可以。
下面给出一个源文件的示例,hello.cpp 。
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/4283cd4bbba41b87.png)
2. 什么是可执行文件
可执行文件 (executable file) 指的是可以由操作系统进行加载执行的文件。不同的操作系统,可执行文件的后缀名不同。在windows操作系统下, .exe文件是非常常见的可执行程序。
其实我并不关心可执行文件的后缀名是什么,我关心的是可执行文件中的内容是什么,为啥这个文件可以被执行。
2.1 为什么可执行文件可以执行
正如我们所知道的,CPU 是计算机的大脑。计算机是通过 CPU 执行程序的。程序员编写的源文件本质上就是文本文件,因此是不可以被 CPU 执行的。CPU 只能解析并运行转换为本地代码的程序内容。不论源代码是用什么编程语言编写的,最后都要转换成本地代码。计算机只能运行本地代码。
那么问题来了,什么是本地代码?简单来说,本地代码就是可执行文件中的内容。
2.2 可执行文件的内容
首先来看下可执行文件中到底有什么。通过编译器,hello.cpp 被编译生成了 hello.exe 文件。用记事本打开 hello.exe 文件,结果如下图所示,是一堆人类理解不了的乱码,如果转为 16 进制,则是一些数值。
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/e3aa5425383ba10d.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5MjIwMzM0,size_16,color_FFFFFF,t_70)
十六进制格式的可执行文件类似下图。如果想查看可执行文件的十六进制,可以参考以下经验。
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/99b88427bc9ce0dc.webp?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5MjIwMzM0,size_16,color_FFFFFF,t_70)
3. 编译器
现在我们知道了,程序员编写的源代码通过编译器可以转换为计算机可以执行的本地代码。现实生活中,程序员可以采用不同的编程语言编写代码,同样编写好的程序也可以运行在不同的计算机上(CPU)。那么编译器是如何实现这一转换过程的呢?
3.1 不同种类的编译器
不同的编程语言对应有不同的编译器,例如:负责将 C 语言编写的源代码转换为本地代码的编译器,被称为 C 编译器。
不同类型的 CPU 也会生成不同类型的本地代码,例如:x86 系列的编译器。
不同运行环境下的编译器,例如:Windows 用的 C 编译器, Linux 用的 C 编译器。因为编译器本身也是一种程序,所以需要考虑运行环境。
3.2 交叉编译
交叉编译是嵌入式开发中非常常见的一个名词。简单来说,交叉编译是指在一个平台上生成另一个平台上的可执行代码。例如:在 x86 平台上,编译生成能运行在 ARM 平台的程序。
交叉编译器就是用来交叉编译的。交叉编译与本地编译不同,本地编译是指在当前编译平台下,编译生成的程序只能在本地平台运行。例如:在 x86 平台上编译生成针对 x86 平台本身的可执行程序。交叉编译生成的是针对目标平台的程序,因而不能在当前编译平台下运行。
4. 如何从源文件到可执行文件
编译器兼具编译和链接两个功能。源代码通过编译后会生成扩展名为 .obj 的目标文件,里面的内容是本地代码。
4.1 链接
链接是指将多个目标文件结合,生成一个 EXE 文件的处理。链接器就是负责链接的程序。链接时需要源代码对应的目标文件和程序的启动。程序的启动也是一个目标文件,里面记述了和所有程序起始位置相结合的处理内容,它是由编译器提供的 .obj 文件。即使程序不调用其他目标文件的函数,也必须要进行链接,和启动结合起来。
4.2 库文件
为了简化链接器参数指定多个目标文件这一过程,库文件应运而生。例如: import32 .lib 就是一个库文件。库文件是多个目标文件的集合,链接器指定库文件后,会从库文件中抽取需要的目标文件,和其他目标文件结合生成 EXE 。库文件中包含的函数被称为标准函数,采用库文件提供函数可以不公开函数的源码。
如果链接时,不指定相应的库文件,链接器就会报错。错误消息一般为:无法解析的外部符号。 也就是说,无法找到记录目的变量和函数的目标文件,因而无法链接。
4.3 DLL文件
Windows 为应用提供了函数的形式的接口 API(Application Programming Interface)。API 本质上是 Windows 提供的一种函数,但它不是标准函数。API的目标文件被存储在 DLL (dynamic link library)中,DLL 文件是程序运行时动态结合的文件,它的扩展名为 .dll ,它本质上是一种特殊的库文件。
4.4 导入库
如果一个 .lib 文件中仅存储着信息,如:import32.lib 中仅存储两个信息,MessageBox() 在 user32.dll 这个 DLL 文件中,存储着 DLL 文件的文件夹信息, 而 MessageBox() 目标文件的实体实际并不存在。类似 import32.lib 这样的库文件则称为导入库。
4.5 静态链接库
存储着目标文件实体,并直接和 EXE 文件结合的库文件形式称为 静态链接库。
为了方便理解,下图给出了 Windows 中编译和链接机制。
![在这里插入图片描述](https://img.php1.cn/3cd4a/1eebe/cd5/d84f9786330d9e41.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM5MjIwMzM0,size_16,color_FFFFFF,t_70)
参考链接
- 如何使用Notepad++工具查看文件的十六进制
- 交叉编译详解 一 概念篇
- 程序是怎样跑起来的
扩展链接
- dll的两种调用方式,lib与dll区别