Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

编译与解释

复习

  • 函数调用与运行栈:保存参数、局部变量和返回地址
  • 程序在内存里的样子:代码区、数据区、栈和堆的初步全景
  • 从源代码到可执行文件:编译、汇编和链接的完整流水线

TL;DR

  • 程序变成机器指令,有两条基本道路:编译和解释
  • 编译:先整体翻译成机器码,再运行;解释:边读边执行
  • 编译通常更快,解释通常更灵活、更跨平台
  • 现实中很多语言走的是中间路线:字节码加虚拟机,还会即时编译热点代码

正文

  前面那条流水线,默认了“先把整个程序翻译完,再运行”。但语言变成机器指令,不一定只有这一种打法。

两条路

  假设你有一本外文书要读,有两种办法:

  • 先请人 整本翻译 成中文,之后你随便翻;翻译只要做一次,读起来最快
  • 请一位 口译 坐在旁边,你读一句、他翻一句;不用等整本翻完,但每次读都要他在场

  程序变成指令,也正好对应这两种办法。

  编译 (compile):把整个程序一次性翻译成机器码,产出可执行文件。之后运行就与编译器无关,直接跑机器码, 。代价是:换了 CPU 或系统,往往得 重新编译 一份。

  解释 (interpret):不产出完整的机器码,而是由一个 解释器 (interpreter)读程序、边读边执行。程序本身不需要为每个平台重新翻译——只要有对应的解释器就行, 灵活、跨平台 ;但执行时要一边读一边翻,通常 更慢

编译解释
翻译时机运行之前,一次翻完运行时,边读边翻
产出机器码,可执行文件不产出机器码
速度通常更快通常更慢
可移植换平台要重新编译有解释器就能跑

各有各的用武之地

  编译的程序跑得快,适合对性能要求高的场合;解释的语言写起来、改起来、跑起来都更省事,适合快速开发和跨平台分发。

  但“编译一定快、解释一定慢”也不绝对。解释器可以优化,编译器也可能把程序优化得很糟。真正决定快慢的,是翻译出来的代码质量,而不只是用了哪种方式。

中间的路线

  现实中,很多语言既不完全编译,也不纯粹解释,而是走中间路线:

  • 字节码加虚拟机 :先把源码翻译成一种与具体机器无关的简化指令,叫 字节码 (bytecode);运行时再靠一个 虚拟机 (virtual machine)解释执行字节码。字节码比机器码通用,虚拟机负责“最后一公里”。
  • 即时编译 (JIT,Just-In-Time):干脆在运行过程中,把其中反复执行的“热点”代码继续翻译成本地机器码。这样既有解释的灵活,又能在关键处拿到编译的速度。
源代码 ──编译器──> 字节码 ──虚拟机(可含即时编译)──> 运行

  所以,编译和解释更像一条连续谱的两端,而不是非此即彼。很多我们熟悉的语言,都站在谱的中间某处。

  到这里,“程序如何变成机器能跑的东西”大致讲清了。可还有一个更大的问题没碰:这段程序由谁装进内存?它想用键盘、屏幕时找谁?如果同时有好几个程序,它们又怎么分 CPU 和内存?这些,都是操作系统要回答的。下一章,我们先看看“没有操作系统”的日子有多难。

思考题

  同一段程序,用编译方式运行和用解释方式运行,哪种启动更快、哪种“运行中”更快?如果程序只运行一次,哪种可能更划算?

小结

知识点

  • 编译:先翻译成机器码,再运行
  • 解释:由解释器边读边执行
  • 编译通常更快,解释通常更灵活、可移植
  • 字节码、虚拟机与即时编译是中间的路线

参考资料

  1. Wikipedia(zh):编译器:编译型语言
  2. Wikipedia(zh):解释器:解释型语言
  3. Wikipedia(zh):字节码:字节码与虚拟机
  4. Wikipedia(zh):即时编译:JIT

思考题答案(仅供参考)

  编译方式要先花时间把整段程序翻成机器码,启动相对慢;但翻完之后每一步都直接跑机器码,运行中通常更快。解释方式省去了整体翻译,启动往往更快,但运行中每条都要边读边翻,慢一些。如果程序只运行一次、而且很快结束,解释可能更划算;如果要长时间反复运行,编译(尤其是即时编译)更值。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪