从源文件到可执行文件

问题

  1. CPU可以解析和运行的程序形式称为什么代码?

    本地代码

  2. 将多个目标文件结合生成EXE文件的工具称为什么?

    链接器

  3. 扩展名为.obj的目标文件的内容,是源代码还是本地代码?

    本地代码

  4. 把多个目标文件收录在一起的文件称为什么?

    库文件

  5. 仅包含Windows的DLL文件中存储的函数信息的文件称为什么?

    导入库

  6. 在程序运行时,用来动态申请分配的数据和对象的内存区域称为什么?

计算机只能运行本地代码

用某种编程语言编写的程序就称为源代码,保存源代码的文件称为源文件。因为源文件是简单的文本文件,所以用文本编辑器就可以编写。

用任何编程语言编写的源代码,最后都要翻译成本地代码,否则CPU就不能理解。

本地代码的内容

Windows中EXE文件的程序内容,使用的就是本地代码。

Dump是指把文件的内容,每个字节用2位十六进制来表示的方式。本地代码的内容就是各种数值的罗列,每个数值都表示某一个指令或数据。而计算机就是把所有的信息作为数值的集合来处理的。

编译器负责转换源代码

能够把C语言等高级编程语言编写的源代码转换成本地代码的程序称为编译器。每个编写源代码的编程语言都需要其专用的编译器。

编译器首先读入代码的内容,然后再把源代码转换成本机代码。编译器中就好像有一个源代码同本地代码的对应表。但实际上,仅仅靠对应表是无法生成本地代码的。读入的源代码还要经过语法解析、句法解析、语义解析等,才能生成本地代码。

根据CPU类型的不同,本地代码的类型也不同。因而,编译器不仅和编程语言的种类有关,和CPU的类型也是相关的。因为编译器本身也是程序的一种,所以也需要运行环境。此外,还有一种交叉编译器,它生成的是和运行环境中的CPU不同的CPU所使用的本地代码。

仅靠编译是无法得到可执行文件的

编译器转换源代码后,就会生成本地文件。不过,本地文件是无法直接运行的。为了得到可以运行的EXE文件,编译之后还需要进行链接。编译后生成的不是EXE文件,而是扩展名为.obj的目标文件。把多个目标文件结合,生成一个EXE文件的处理就是链接,运行链接的程序就称为链接器。

启动及库文件

c0w32.obj这个目标文件记述的是同所有程序起始位置相结合的处理内容,称为程序的启动。即使程序不调用其他目标文件的函数,也必须要进行链接,并和启动结合起来。

库文件指的是把多个目标文件集成保存到一个文件中的形式。链接器指定库文件后,就会从中把需要的目标文件抽取出来,并同其他目标文件结合生成EXE文件。

外部符号是指其他目标文件中的变量或函数。错误消息“无法解析的外部符号”表示的是无法找到记述着目的变量及函数的目标文件,因而无法进行链接的意思。

sprintf等函数,不是通过源代码形式而是通过库文件形式和编译器一起提供的。这样的函数称为标准函数。之所以使用库文件,是为了简化为链接器的参数指定多个目标文件这一过程。

通过以目标文件的形式或集合多个目标文件的库文件形式来提供函数,就可以不用公开标准函数的源代码内容。由于标准函数的源代码是编译器厂商的贵重财产,因此若被其他公司任意专用的话,可能会造成一些损失。

DLL文件及导入库

Windows以函数的形式为应用提供了各种功能。这些形式的函数称为API。Windows中,API的目标文件,并不是存储在通常的库文件中,而不是存储在名为DLL文件的特殊库文件中。DLL文件是程序运行时动态结合的文件。而把.lib文件称为导入库。

与之相反,存储着目标文件的实体,并直接和EXE文件结合的库文件形式称为静态链接库。

可执行文件运行时的必要条件

EXE文件中给变量和函数分配了虚拟的内存地址。在程序运行时,虚拟的内存地址会转换成实际的内存地址。链接器会在EXE的开头,追加转换内存地址所需的必要信息,这个信息称为再配置信息。

EXE文件的再配置信息,就成为了变量和函数的相对地址。相对地址表示的是相对于基点地址的偏移量。在源代码中,虽然变量和函数是在不同位置分散记述的,但在链接后的EXE文件中,变量和函数就会变成一个连续排列的组。而各组基点的内存地址则是在程序运行时被分配的。

程序加载时会生成栈和堆

EXE文件的内容分为再配置信息、变量组和函数组。不过,当程序加载到内存后,除此之外还会额外生成两个组,那就是栈和堆。栈是用来存储函数内部临时使用的变量(局部变量),以及函数调用时所用的参数的内存区域。堆是用来存储程序运行时的任意数据集对象的内存领域。

EXE文件中并不存在栈和堆的组。栈和堆需要的内存空间是在EXE文件加载到内存后开始运行时得到分配的。因此,内存中的程序,就是由用于变量的内存空间、用于函数的内存空间、用于栈的内存空间、用于堆的内存空间这4部分构成的。

栈中对数据进行存储和舍弃的代码,是由编译器自动生成的,因此不需要程序员的参与。使用栈的数据的内存空间,每当函数被调用时都会得到申请分配,并在函数处理完毕后自动释放。堆的内存空间,则需要根据程序员编写的程序,来明确进行申请分配或释放。

根据编程语言的不同,对堆用的内存空间进行申请分配和释放的方法也是不同的:

  • C语言:malloc函数申请分配、free函数释放
  • C++:new运算符申请分配、delete运算符释放

如果没有在程序中明确释放堆的内存空间,那么即使在处理完毕后,该内存空间仍会一致残留,这个现象称为内存泄漏。如果内存泄漏一致存在的话,就有可能会造成内存不足而导致宕机。

有点难度的问题

  1. 编译器和解释器有什么不同?

    编译器是在运行前对所有源代码进行解释处理的,解释器则是在运行时对源代码的内容一行一行地进行解释处理的。

  2. 分割编译指的是什么?

    将整个程序分成多个源代码来编写,然后分别进行编译,最后链接成一个EXE文件。这样每个源代码都相对变短,便于程序管理。

  3. Build指的是什么?

    根据开发工具种类的不同,有点编译器可以通过选择Build菜单来生成EXE文件。这种情况下,Build是指连续执行编译和链接。

  4. 使用DLL文件的好处是什么?

    DLL文件中的函数可以被多个程序共用。因此,借助该功能可以节约内存和磁盘。此外,在对函数的内容进行修正时,不需要重新链接使用这个函数的程序。(减少维护成本)

  5. 不链接导入库的话就无法调用DLL文件中 函数吗?

    通过使用LoadLibrary以及GetProcAddress这些API(显式调用),即使不链接导入库,也可以在程序运行时调用DLL文件中的函数。不过使用导入库(隐式调用)更简单一点。

  6. 叠加链接指的是什么?

    将不会同时执行的函数,交替加载到同一个地址中运行。通过使用叠加链接器即可实现。在计算机中配置的内存容量不动的MS-DOS时代,经常使用叠加链接。

  7. 和内存管理相关的垃圾回收机制指的是什么呢?

    垃圾回收机制garbage collection指的是对处理完毕后不再需要的堆内存空间的数据和对象进行清理,释放它们所使用的内存空间。在C++的基础上开发出来的Java及C#这些编程语言中,程序运行环境会自动进行垃圾回收。这样就可以避免由于程序的疏忽而造成内存泄漏了。

posted @ 2021-03-29 15:19  睿阳  阅读(382)  评论(0编辑  收藏  举报