本章作者首先详细描述了从编写源代码到生成可执行文件的过程,为我们展现了一个程序从无到有的诞生之旅。
首先,作者提到了源文件,(用某种编程语言编写的程序就称为源代码,保存源代码的文件称为源文件)也就是我们通常编写的代码文件,如C、C++、Python等语言编写的文件。这些文件只是我们思想的载体,是告诉计算机应该做什么的指令集合。然而,计算机并不能直接理解这些源文件(CPU能直接解析并运行的不是源代码而是本地代码的程序),它只能理解机器语言,也就是二进制代码。
那么,如何将这些源文件转化为计算机可以理解的机器语言呢?这就需要编译器的介入。编译器的作用是将源文件转化为机器语言。具体来说,编译器首先会将源文件转化为中间代码,这是一种与平台无关的、更为通用的代码。接着,编译器再将中间代码转化为目标代码,也就是机器语言(每个编写源代码的编程语言都需要其专用的编译器)。
最后,编译器将生成的目标代码、所需的库文件和其他资源通过链接器(链接器的主要任务是将各个编译单元(如不同的源文件或库文件)链接在一起,形成一个完整的可执行文件。链接器会处理各个编译单元之间的符号引用关系,确保程序在运行时能够正确地找到所需的函数和数据。)打包为一个可执行文件。这个可执行文件就包含了程序运行所需的所有信息,可以被计算机直接执行。链接器还有一个作用那就是在程序运行时,虚拟的内存地址会转换成实际的内存地址。链接器会在EXE文件的开头,追加转换内存地址所需的必要信息。这个信息称为再配置信息。此外,当程序加载到内存后,额外生成两个组,那就是栈和堆。栈是用来存储函数内部临时使用的变量(局部变量 ),以及函数调用时所用的参数的内存区域。堆是用来存储程序运行时的任意数据及对象的内存领域。
在本章的末尾,作者针对新手对本章中的一些技术做了解释,如下:
编译器和解释器有什么不同?
编译器是在运行前对所有源代码进行解释处理的。而解释器则是在运行时对源代码的内容一行一行地进行解释处理的。
“分割编译”指的是什么?
将整个程序分为多个源代码来编写,然后分别进行编译,最后链接成一个EXE文件。这样每个源代码都相对变短,便于程序管理。
“Build”指的是什么?
根据开发工具种类的不同,有的编译器可以通过选择“Build”菜单来生成EXE文件。这种情况下,Build指的是连续执行编译和链接。
使用DLL文件的好处是什么?
DLL 文件中的函数可以被多个程序共用。因此,借助该功能可以节约内存和磁盘。此外,在对函数的内容进行修正时,还不需要重新链接(静态链接)使用这个函数的程序。
不链接导入库的话就无法调用DLL文件中的函数吗?
通过使用LoadLibrary()及GetProcAddress()这些API,即使不链接导入库,也可以在程序运行时调用DLL文件中的函数。不过使用导入库更简单一些。
“叠加链接”这个术语指的是什么?
将不会同时执行的函数,交替加载到同一个地址中运行。通过使用“叠加链接器”这一特殊的链接器即可实现。在计算机中配置的内存容量不多的MS-DOS时代,经常使用叠加链接。
和内存管理相关的“垃圾回收机制”指的是什么呢?
垃圾回收机制(garbage collection)指的是对处理完毕后不再需要的堆内存空间的数据和对象 进行清理,释放它们所使用的内存空间。这里把不需要的数据比喻为了垃圾。进行该处理时,C语言用的是free()函数,C++用的是delete运算符。在C++的基础上开发出来的Java及C#这些编程语言中,程序运行环境会自动进行垃圾回收。这样就可以避免由于程序员的疏忽(忘了记述内存的释放处理)而造成内存泄露了。
总的来说,第八章为我们详细展示了从源文件到可执行文件的转化过程,让我们对程序的生成和运行有了更深入的理解。