编译语言与口译语言


284

我正在尝试更好地理解它们之间的区别。我在网上找到了很多解释,但是它们倾向于抽象的差异,而不是实际的含义。

我的大部分编程经验都来自CPython(动态的,解释的)和Java(静态的,编译的)。但是,我知道还有其他种类的解释和编译语言。除了可以从以编译语言编写的程序中分发可执行文件这一事实之外,每种类型是否都有优点/缺点?通常,我听到人们争辩说解释语言可以交互使用,但是我相信编译语言也可以具有交互实现,对吗?


32
您为此选择了最差的语言。两者都是字节编译的。它们之间唯一真正的区别是JITer,甚至Python也有一部分(psyco)。
伊格纳西奥·巴斯克斯

1
交互式编译语言的一个很好的例子是Clojure-一切都已完全编译(首先是JVM,然后是通过JIT的本机代码)。但是,许多重新编译是动态进行的,并且开发通常是在交互式REPL Shell中完成的,您可以在其中评估运行环境中所需的任何功能。
mikera 2010年

标准ML是另一种交互式编译语言。内置的编译器也会发出真正的本机代码。
Donal Fellows 2010年


Answers:


458

编译语言是一种程序,一旦编译,该程序就会在目标计算机的指令中表示出来。例如,源代码中的加号“ +”操作可以直接转换为机器代码中的“ ADD”指令。

一种解释语言是其中所述指令不被目标机器直接执行,而是读取和执行通过一些其它方案(其通常写入本机机器的语言)。例如,解释器将在运行时识别相同的“ +”操作,然后使用适当的参数调用其自己的“ add(a,b)”函数,然后执行机器代码“ ADD”指令。

您可以使用解释语言或编译语言来做任何事情,反之亦然-它们都是图灵完整的。但是,两者在实现和使用上都有优点和缺点。

我将完全概括(纯粹主义者请原谅!),但是,粗略地讲,这是编译语言的优点:

  • 通过直接使用目标计算机的本机代码来提高性能
  • 在编译阶段进行功能强大的优化的机会

以下是解释语言的优点:

  • 易于实现(编写好的编译器非常困难!)
  • 无需运行编译阶段:可以“即时”直接执行代码
  • 可以更方便地使用动态语言

请注意,诸如字节码编译之类的现代技术增加了一些额外的复杂性-此处发生的是,编译器针对的是“虚拟机”,该“虚拟机”与底层硬件不同。然后可以在以后的阶段再次编译这些虚拟机指令,以获取本机代码(例如,由Java JVM JIT编译器完成)。


1
并非所有编译语言都需要缓慢的编译阶段。严重的Common Lisp实现是编译器,它们通常不理会解释器,而宁愿只是快速地实时编译。另一方面,Java确实需要一个编译步骤,并且通常是可见的。
David Thornley,2010年

2
@Kareem:JIT编译器仅执行1)和2)一次 -之后,它一直是本机代码。每次调用代码时,解释器都需要同时执行1)和2)(这可能很多很多次)。因此,随着时间的流逝,JIT编译器大获全胜。
mikera 2013年

3
是的,在整个程序执行期间的某个时刻,字节码已转换为机器代码(与传统编译器的情况一样,在程序执行之前)。但是给定的一段代码在整个程序执行期间可能执行一千万次以上。从字节码到机器码(可能)仅编译一次。因此,JIT的运行时开销很小,对于长时间运行的程序可以忽略。在JIT编译器完成其工作之后,您将有效地一直在运行纯机器代码。
mikera

2
这实际上是错误的二分法。语言没有内在的东西可以使它编译为我们的解释。这无非是一种普遍存在的误解。许多语言都有实现,所有语言都可以有。
mmachenry,2014年

2
@mmachenry这不是错误的二分法。“编程语言”包括设计和实现。虽然从理论上讲,既可以编译和解释给定的语言定义,但在实际操作中,实现存在很大差异。例如,还没有人解决如何有效地编译某些语言结构的问题-这是一个开放的研究问题。
mikera 2014年

99

语言本身既不会编译也不会解释,而仅是语言的特定实现。Java是一个完美的例子。有一个基于字节码的平台(JVM),一个本机编译器(gcj)和一个Java超集的插入器(bsh)。那么,Java现在是什么?字节码编译,本机编译还是解释?

Scala,Haskell或Ocaml是经过编译和解释的其他语言。这些语言中的每一种都有一个交互式解释器,以及用于字节码或本机代码的编译器。

因此,通常通过“编译”和“解释”对语言进行分类没有多大意义。


3
我同意。或说:存在一些本机编译器(创建供CPU使用的机器代码)和非本机编译器(创建令牌化的东西(即中间代码),一些即时编译器在之前将其编译为机器代码( (或运行时)一次,并且有“真正的”非编译器,它们从不产生机器代码,也从不让CPU运行代码。后者是口译员。如今,在编译时直接生成机器(CPU)代码的本机编译器变得越来越稀有。Delphi / Codegear是最好的幸存者之一。
TheBlastOne

56

开始思考:过去的爆炸

很久很久以前,曾经有计算解释器和编译器。各种大惊小怪的结果使一个人的优胜劣汰。普遍的观点在当时是沿着线的东西:

  • 口译员:快速开发(编辑和运行)。执行速度很慢,因为每个语句每次执行时都必须将其解释为机器代码(想想这对于执行数千次循环意味着什么)。
  • 编译器:开发缓慢(编辑,编译,链接和运行。编译/链接步骤可能需要花费大量时间)。快速执行。整个程序已经在本机代码中。

在解释的程序和编译的程序之间存在一个或两个数量级的运行时性能差异。其他区别点,例如代码的运行时可变性,也引起了一些关注,但主要区别在于运行时性能问题。

如今,景观已发展到某种程度,以至于汇编/解释的区别几乎是无关紧要的。许多编译语言要求运行时服务不是完全基于机器代码的。而且,大多数解释语言在执行之前都会被“编译”为字节码。字节码解释器可能非常高效,并且从执行速度的角度来看,可以与某些编译器生成的代码相媲美。

经典的区别是,编译器使用某种运行时系统生成本机代码,解释器读取源代码并动态生成代码。如今,几乎没有经典的解释器了-几乎所有经典的解释器都编译成字节码(或其他半编译状态),然后在虚拟“机器”上运行。


1
尼斯-上一段总结不错-谢谢!
ckib16 '18

26

极端简单的情况:

  • 编译器将生成目标计算机的本机可执行文件格式的二进制可执行文件。该二进制文件包含除系统库以外的所有必需资源。它无需任何准备和处理即可运行,并且像闪电一样运行,因为该代码是目标计算机上CPU的本机代码。

  • 解释器将在循环中向用户显示提示,用户可以在其中输入语句或代码,并且在命中RUN或等效命令时,解释器将检查,扫描,解析并以解释方式执行每一行,直到程序运行至停止点或错误为止。因为每一行都是独立处理的,并且解释器不会从以前的行中“学到”任何东西,所以每行每次都需要将人类可读的语言转换为机器指令,所以这太慢了。从好的方面来说,用户可以通过各种方式检查程序并与之交互:更改变量,更改代码,在跟踪或调试模式下运行……等等。

顺便说一句,让我解释一下生活不再那么简单了。例如,

  • 许多解释器会预编译给出的代码,因此不必一次又一次地重复翻译步骤。
  • 一些编译器不编译为特定于CPU的机器指令,而是编译为字节码,这是一种虚拟机器的人造机器代码。这使编译后的程序更具可移植性,但是在每个目标系统上都需要一个字节码解释器。
  • 字节码解释器(我现在在这里看着Java)倾向于在执行之前为目标部分的CPU重新编译它们获得的字节码(称为JIT)。为了节省时间,通常只对经常运行的代码(热点)执行此操作。
  • 一些看起来和行为像解释器的系统(例如,Clojure)会立即编译它们获得的任何代码,但允许以交互方式访问程序环境。从根本上来说,这就是二进制编译器为解释器带来的便利。
  • 一些编译器并没有真正编译,只是预消化和压缩代码。我听说前阵子就是Perl的工作方式。因此,有时编译器只是在做一些工作,而且大部分仍在解释中。

最终,如今,解释与编译是一个折衷方案,花费(一次)编译的时间通常会因更好的运行时性能而获得回报,但是解释性环境为交互提供了更多机会。编译与解释主要是关于如何“理解”程序的工作如何在不同的过程之间进行划分的问题,而如今,由于语言和产品试图同时兼顾两者的优势,这条线有些模糊。


23

来自http://www.quora.com/What-is-the-difference-between-compiled-and-interpreted-programming-languages

这没有什么区别,因为“编译程序设计语言”和“解释程序设计语言”不是有意义的概念。任何编程语言(实际上是任何一种编程语言)都可以解释或编译。因此,解释和编译是实现技术,而不是语言的属性。

解释是一种技术,通过该技术,另一个程序(解释器)代表正在解释的程序执行操作以使其运行。如果您可以想象阅读程序并按照程序说的做一步一步,例如在草稿纸上说,那也是解释器的工作。解释程序的常见原因是解释器相对容易编写。另一个原因是,解释器可以监视程序在运行时试图执行的操作,以执行安全性策略。

编译是一种技术,通过该技术可以将用一种语言(“源语言”)编写的程序转换为另一种语言(“目标语言”)的程序,这希望与原始程序具有相同的含义。在进行翻译时,编译器通常还会尝试以使目标程序更快的方式(不改变其含义!)对程序进行转换。编译程序的一个常见原因是,有一种很好的方法可以以目标语言快速运行程序,而无需一路解释源语言。

根据上述定义,您可能已经猜到这两种实现技术不是互斥的,甚至可能是互补的。传统上,编译器的目标语言是机器代码或类似的东西,它表示特定计算机CPU可以理解的任何数量的编程语言。然后,机器代码将“运行在金属上”(尽管如果看起来足够接近,人们可能会发现“金属”的工作原理很像解释器)。但是,如今,使用编译器生成要解释的目标代码已经非常普遍了,例如,Java曾经(有时仍然这样做)就是这种方式。有些编译器会将其他语言翻译成JavaScript,然后通常在网络浏览器中运行,这些浏览器可能会解释JavaScript,或将其编译为虚拟机或本机代码。我们还提供了机器码解释器,可用于在另一种机器上模拟一种硬件。或者,可以使用编译器生成目标代码,然后该目标代码将成为另一编译器的源代码,后者甚至可以及时在内存中编译代码以使其运行,然后依次运行。。。你明白了。有很多方法可以组合这些概念。


您能修正一下这句话吗:“有些编译器会将其他语言翻译成JavaScript,然后通常在网络浏览器中运行,它们可能会解释JavaScript或将其编译为虚拟机或本地代码。”
Koray Tugay,2015年

搞定了。另一个常见的错误是将一种语言的有用性归因于其现有的API。
Little Endian

10

与已编译的源代码相比,已解释的源代码的最大优势是PORTABILITY

如果您的源代码已编译,则需要为要在其上运行程序的每种类型的处理器和/或平台编译一个不同的可执行文件(例如,一个用于Windows x86,一个用于Windows x64,一个用于Linux x64,等等。上)。此外,除非您的代码完全符合标准并且不使用任何平台特定的功能/库,否则您实际上将需要编写和维护多个代码库!

如果您的源代码被解释,则只需编写一次即可,并且可以由任何平台上的适当解释器来解释和执行它!它是便携式的!需要注意的是一个解释器本身是一个可执行程序编写和编译为特定平台。

编译后代码的一个优点是,它向最终用户隐藏了源代码(可能是知识产权),因为您部署了晦涩的二进制可执行文件,而不是部署原始的人类可读源代码。


1
按照这种说法,Java不能被认为是“编译语言”,但是它的编译阶段具有编译(类型检查,早期错误检测等)的优势,并且可以生成可在每个OS上使用Java运行的字节码。提供的虚拟机。
RogelioTriviño17年

7

编译器和解释器完成相同的工作:将编程语言翻译为另一种编程语言,通常更接近硬件,通常指导可执行的机器代码。

传统上,“编译”是指这种翻译一次完成,由开发人员完成,然后将生成的可执行文件分发给用户。纯粹的例子:C ++。编译通常花费很长时间,并尝试进行大量昂贵的优化,以使生成的可执行文件运行更快。最终用户没有工具和知识来自己编译东西,并且可执行文件通常必须在各种硬件上运行,因此您不能进行许多针对硬件的优化。在开发过程中,单独的编译步骤意味着更长的反馈周期。

传统上,“已解释”是指当用户要运行程序时,翻译是“即时”进行的。纯粹的例子:香草PHP。天真的解释器每次运行时都必须解析和翻译每段代码,这使其非常慢。它无法进行复杂,成本高昂的优化,因为它们所花费的时间比执行所节省的时间还要长。但是它可以充分利用其运行的硬件的功能。缺少单独的编译步骤可减少开发过程中的反馈时间。

但是如今,“编译与解释”已不是一个黑白问题,介于两者之间。天真的,简单的口译员已经绝迹了。许多语言使用两步过程,其中将高级代码转换为平台无关的字节码(解释起来更快)。然后,您将拥有“及时编译器”,每个程序运行一次最多编译一次代码,有时缓存结果,甚至可以明智地决定解释很少运行的代码,并对运行频繁的代码进行强大的优化。在开发过程中,调试器甚至可以针对传统编译语言在正在运行的程序中切换代码。


1
但是,C ++的编译模型是从C继承的,设计时并未考虑模板等功能。这种笨拙对C ++的长编译时间的贡献比其他任何因素都大得多-使其成为一个糟糕的例子。

4

首先,澄清一下,Java不是完全以C ++的方式静态编译和链接的。它被编译成字节码,然后由JVM解释。JVM可以及时对本机语言进行编译,但不必这样做。

更重要的是:我认为互动是主要的实际差异。由于所有内容均已解释,因此您可以摘录一小段代码,然后针对环境的当前状态进行解析和运行。因此,如果您已经执行过初始化变量的代码,则可以访问该变量,等等。它确实可以将其自身用于诸如功能样式之类的事情。

但是,解释会花费很多,尤其是当您拥有一个具有大量引用和上下文的大型系统时。根据定义,这是浪费的,因为可能必须两次解释和优化相同的代码(尽管大多数运行时对此都有一些缓存和优化)。尽管如此,您仍然需要支付运行时成本,并且经常需要运行时环境。您也不太可能看到复杂的过程间优化,因为目前它们的性能还不够互动。

因此,对于那些变化不大的大型系统,对于某些语言而言,预编译和预链接所有内容更有意义,请执行您可以做的所有优化。最终将获得非常精益的运行时,该运行时已针对目标计算机进行了优化。

至于生成可执行文件,与恕我直言无关。您通常可以使用编译语言创建可执行文件。但是,您也可以使用解释语言创建可执行文件,只是解释器和运行时已打包在可执行文件中,并且对您隐藏了。这意味着您通常仍需支付运行时成本(尽管我确信对于某些语言,有一些方法可以将所有内容转换为树可执行文件)。

我不同意所有语言都可以互动。某些语言(例如C)与机器和整个链接结构紧密相关,因此我不确定您是否可以构建有意义的完整交互式版本


C并不真正与“机器”联系在一起。C的语法和语义非常简单。实现C解释器不是特别困难,只是非常耗时(因为还必须实现标准库)。顺便说一句,可以将Java编译为本机代码(使用gcj)。
lunaryorn

@lunaryorn:我不同意GCJ。GCJ仅给您一个基于可执行文件的环境。“已编译的应用程序与GCJ运行时libgcj链接,后者提供了核心类库,垃圾回收器和字节码解释器”
Uri 2010年

2
GCJ 确实会生成本地机器代码,而不仅仅是产生具有嵌入式解释器和字节码的可执行环境。libgcj提供了一个字节码解释器,以支持从本机代码到Java字节码的调用,而不是解释已编译的程序。如果libgcj没有提供字节码解释器,则GCJ将不符合Java规范。
lunaryorn

@lunaryorn:啊。好的,我很感谢您的澄清,并表示纠正。我们主要在Windows环境中使用Java,因此多年来我都没有尝试过gcj。
乌里(Uri)


2

给出实际答案相当困难,因为区别在于语言定义本身。可以为每种编译语言构建解释器,但不可能为每种解释语言构建编译器。语言的形式定义非常重要。从而使理论上的信息学方面的东西更受大学的欢迎。


1
当然,您可以为解释语言构建编译器,但是编译后的机器代码本身就是运行时的镜像。
艾登·贝尔

2

Python图书©2015 Imagine Publishing Ltd,仅通过第10页中提到的以下提示来区分差异:

一种解释性语言(例如Python)是一种将源代码转换为机器代码,然后在每次程序运行时执行的语言。这与诸如C之类的编译语言不同,后者仅将源代码转换为机器代码一次-每次程序运行时都会执行生成的机器代码。


1

编译是从以编译的编程语言编写的代码创建可执行程序的过程。编译允许计算机运行和理解程序,而无需使用用于创建该程序的编程软件。编译程序时,通常是针对特定平台(例如IBM平台)编译的,该平台可与IBM兼容计算机一起使用,但不适用于其他平台(例如Apple平台)。第一个编译器是由Grace Hopper在哈佛Mark I计算机上开发的。今天,大多数高级语言将包括其自己的编译器或可用的工具包,可用于编译程序。与Java一起使用的编译器的一个很好的例子是Eclipse,而与C和C ++一起使用的编译器的一个例子是gcc命令。


0

简短(不精确)的定义:

编译语言:整个程序立即转换为机器代码,然后由CPU运行机器代码。

解释的语言:逐行读取程序,一旦读取一行,CPU就会执行该行的机器指令。

但是,实际上,如今只有很少的语言是纯编译的或纯解释的,通常是混合的。有关图片的详细说明,请参见以下线程:

编译和解释之间有什么区别?

或我后来的博客文章:

https://orangejuiceliberationfront.com/the-difference-between-compiler-and-interpreter/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.