Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

第四十五章:从源代码到可执行文件

复习

  • 程序运行时,机器指令、数据、堆和栈会占据不同内存区域。
  • 高级语言源代码必须经过翻译,CPU 才能执行。

TL;DR

  • 编译器、汇编器和链接器会协作,把源代码制成可执行文件。
  • 一个程序常由多个源文件和现成库共同组成。
  • 可执行文件不仅有指令,也记录了装载和组织程序所需的信息。

正文

  我们写下的 total = apples + pears 不是 CPU 能装入内存的样子。以常见的编译型语言为例,一条典型路线如下:

源代码
  ↓ 编译器
汇编代码
  ↓ 汇编器
目标文件
  ↓ 链接器
可执行文件

  编译器读取高级语言,检查语法和类型,并生成较低层的表示,常常会输出汇编代码或直接输出目标代码。汇编器把其中的汇编指令变成机器码,形成目标文件。目标文件已经有许多机器指令,却未必能独立运行。

  原因是大型程序很少只有一个文件。main 可能在一个源文件中,打印文字的函数在另一个文件中,数学函数又来自一套预先写好的链接器负责把这些片段合在一起,解决“这个函数的定义究竟在哪里”之类的引用,并安排它们在最终文件中的位置。

  得到的可执行文件像一个已经打包好的程序包:除了代码和初始数据,通常还保存格式、入口位置、外部依赖等信息。操作系统以后会读取这些信息,给程序准备内存并从入口开始执行。

  这里的每一步都值得深入研究,我们会在后面的编译原理部分再拆开它们。此刻只要记住:源代码不是被 CPU 直接“运行”,而是经过一连串转换和组合。还有另一条常见路线,并不先制成这样一份本地机器码文件。

思考题

为什么仅把两个目标文件放在同一个文件夹里,通常还不能形成一个程序?

小结

知识点

  • 编译器将高级语言转成更低层的程序表示。
  • 汇编器生成机器码,链接器组合目标文件和库。
  • 可执行文件是可被装载和启动的程序包。

参考资料

  • Alfred Aho 等,《编译原理》
  • GNU Compiler Collection 文档

思考题答案(仅供参考)

  目标文件之间仍可能保留未解决的函数和数据引用。链接器需要找到定义、把地址关联起来,并生成统一的最终文件。

协议

本文采用 CC BY-NC-SA 4.0 协议发布。