编译与解释
复习
- 函数调用与运行栈:保存参数、局部变量和返回地址
- 程序在内存里的样子:代码区、数据区、栈和堆的初步全景
- 从源代码到可执行文件:编译、汇编和链接的完整流水线
TL;DR
- 程序变成机器指令,有两条基本道路:编译和解释
- 编译:先整体翻译成机器码,再运行;解释:边读边执行
- 编译通常更快,解释通常更灵活、更跨平台
- 现实中很多语言走的是中间路线:字节码加虚拟机,还会即时编译热点代码
正文
前面那条流水线,默认了“先把整个程序翻译完,再运行”。但语言变成机器指令,不一定只有这一种打法。
两条路
假设你有一本外文书要读,有两种办法:
- 先请人 整本翻译 成中文,之后你随便翻;翻译只要做一次,读起来最快
- 请一位 口译 坐在旁边,你读一句、他翻一句;不用等整本翻完,但每次读都要他在场
程序变成指令,也正好对应这两种办法。
编译 (compile):把整个程序一次性翻译成机器码,产出可执行文件。之后运行就与编译器无关,直接跑机器码, 快 。代价是:换了 CPU 或系统,往往得 重新编译 一份。
解释 (interpret):不产出完整的机器码,而是由一个 解释器 (interpreter)读程序、边读边执行。程序本身不需要为每个平台重新翻译——只要有对应的解释器就行, 灵活、跨平台 ;但执行时要一边读一边翻,通常 更慢 。
| 编译 | 解释 | |
|---|---|---|
| 翻译时机 | 运行之前,一次翻完 | 运行时,边读边翻 |
| 产出 | 机器码,可执行文件 | 不产出机器码 |
| 速度 | 通常更快 | 通常更慢 |
| 可移植 | 换平台要重新编译 | 有解释器就能跑 |
各有各的用武之地
编译的程序跑得快,适合对性能要求高的场合;解释的语言写起来、改起来、跑起来都更省事,适合快速开发和跨平台分发。
但“编译一定快、解释一定慢”也不绝对。解释器可以优化,编译器也可能把程序优化得很糟。真正决定快慢的,是翻译出来的代码质量,而不只是用了哪种方式。
中间的路线
现实中,很多语言既不完全编译,也不纯粹解释,而是走中间路线:
- 字节码加虚拟机 :先把源码翻译成一种与具体机器无关的简化指令,叫 字节码 (bytecode);运行时再靠一个 虚拟机 (virtual machine)解释执行字节码。字节码比机器码通用,虚拟机负责“最后一公里”。
- 即时编译 (JIT,Just-In-Time):干脆在运行过程中,把其中反复执行的“热点”代码继续翻译成本地机器码。这样既有解释的灵活,又能在关键处拿到编译的速度。
源代码 ──编译器──> 字节码 ──虚拟机(可含即时编译)──> 运行
所以,编译和解释更像一条连续谱的两端,而不是非此即彼。很多我们熟悉的语言,都站在谱的中间某处。
到这里,“程序如何变成机器能跑的东西”大致讲清了。可还有一个更大的问题没碰:这段程序由谁装进内存?它想用键盘、屏幕时找谁?如果同时有好几个程序,它们又怎么分 CPU 和内存?这些,都是操作系统要回答的。下一章,我们先看看“没有操作系统”的日子有多难。
思考题
同一段程序,用编译方式运行和用解释方式运行,哪种启动更快、哪种“运行中”更快?如果程序只运行一次,哪种可能更划算?
小结
知识点
- 编译:先翻译成机器码,再运行
- 解释:由解释器边读边执行
- 编译通常更快,解释通常更灵活、可移植
- 字节码、虚拟机与即时编译是中间的路线
参考资料
- Wikipedia(zh):编译器:编译型语言
- Wikipedia(zh):解释器:解释型语言
- Wikipedia(zh):字节码:字节码与虚拟机
- Wikipedia(zh):即时编译:JIT
思考题答案(仅供参考)
编译方式要先花时间把整段程序翻成机器码,启动相对慢;但翻完之后每一步都直接跑机器码,运行中通常更快。解释方式省去了整体翻译,启动往往更快,但运行中每条都要边读边翻,慢一些。如果程序只运行一次、而且很快结束,解释可能更划算;如果要长时间反复运行,编译(尤其是即时编译)更值。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪