编译语言与口译语言

chi*_*der 263 python java compiler-construction interpreter programming-languages

我正在努力更好地理解这种差异.我在网上找到了很多解释,但它们倾向于抽象的差异而不是实际的含义.

我的大部分编程经验都是CPython(动态,解释)和Java(静态,编译).但是,据我所知,还有其他类型的解释和编译语言.除了可执行文件可以从编译语言编写的程序中分发之外,每种类型都有任何优点/缺点吗?通常,我听到有人认为解释性语言可以交互使用,但我相信编译语言也可以有交互式实现,对吗?

mik*_*era 437

编译语言是程序一旦编译就在目标机器的指令中表达的语言.例如,源代码中的附加"+"操作可以直接转换为机器代码中的"ADD"指令.

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

您可以使用编译语言以解释语言执行任何操作,反之亦然 - 它们都是图灵完备的.然而,两者都具有实施和使用的优点和缺点.

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

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

以下是解释语言的优点:

  • 更容易实现(编写好的编译器非常困难!!)
  • 无需运行编译阶段:可以"动态"直接执行代码
  • 动态语言可以更方便

请注意,现代技术(如字节码编译)会增加一些额外的复杂性 - 这里发生的是编译器针对的是与底层硬件不同的"虚拟机".然后可以在稍后阶段再次编译这些虚拟机指令以获得本机代码(例如,由Java JVM JIT编译器完成).

  • 是的,在整个程序执行期间,字节码在某些时候被转换为机器代码*(与程序执行之前相反,就像传统编译器一样).但是在整个程序执行期间,给定的代码片段可能会执行1000万次以上.它(可能)只从字节码到机器代码编译**.因此,JIT的运行时开销很小,对于长时间运行的程序可以忽略.在JIT编译器完成其工作之后,您将有效地运行纯机器代码. (3认同)
  • @Kareem:JIT编译器只执行1)和2)*一次* - 之后它一直是本机代码.每次调用代码时,解释器都需要同时执行1)和2)(可能很多次......).所以随着时间的推移,JIT编译器赢得了很长的利润. (2认同)
  • 这实际上是一种错误的二分法.语言没有任何内在因素可以编译我们的解释.这只不过是一种广泛存在的误解.许多语言都有两种实现方式,所有语言都有. (2认同)
  • @mmachenry 这不是错误的二分法。“编程语言”包括设计和实现。虽然在*理论上*一个给定的语言定义可以被编译和解释,但在*现实世界实践*在实现上存在相当大的差异。例如,还没有人解决如何有效地编译某些语言结构——这是一个开放的研究问题。 (2认同)
  • @mikera 确实如此。编译有好处,解释也有好处。仅仅因为编译器技术正在发展以改进某些语言的功能,并不意味着我们可以谈论使用该功能编译语言的好处。将语言和实现混为一谈会导致我们对为实现选择编译或解释有错误的理解。例如您的评论“[解释器] 可以更方便地使用动态语言” (2认同)

lun*_*orn 95

语言本身既不编译也不解释,只是语言的特定实现.Java就是一个很好的例子.有一个基于字节码的平台(JVM),一个本机编译器(gcj)和一个Java超集(bsh)的插件.那么Java现在是什么?字节码编译,本地编译或解释?

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

因此,通常按"编译"和"解释"对语言进行分类并没有多大意义.

  • 我同意.或者说:有本机编译器(为CPU创建机器代码)和非本机编译器(创建标记化的东西,即中间代码,一些即时编译器编译成机器代码之前(或者在运行期间ONCE),并且存在"真正的"非编译器,它们永远不会产生机器代码,也永远不会让CPU运行代码.后者是口译员.今天,在编译时直接生成机器(CPU)代码的本机编译器变得越来越少见.Delphi/Codegear是最好的幸存者之一. (3认同)

Nea*_*alB 51

从过去开始思考:爆炸

曾几何时,很久很久以前,那里住着计算机翻译和编译器.随着一个人的优点,各种各样的烦恼随之而来.普遍的观点在当时是沿着线的东西:

  • 口译员:快速开发(编辑和运行).执行起来很慢,因为每次执行时都必须将每个语句解释为机器代码(想想这对循环执行数千次的意义).
  • 编译器:开发缓慢(编辑,编译,链接和运行.编译/链接步骤可能需要很长时间).快速执行.整个程序已经在本机机器代码中.

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

今天,景观已发展到如此程度,以至于编译/解释的区别几乎无关紧要.许多编译语言都会调用不完全基于机器代码的运行时服务.此外,大多数解释语言在执行之前被"编译"为字节码.字节码解释器可以非常高效,并且从执行速度的角度来看与一些编译器生成的代码相匹敌.

经典的区别在于编译器使用某种运行时系统生成本机机器代码,解释器读取源代码并生成机器代码.今天很少有经典的解释器 - 几乎所有解释器都编译成字节码(或其他一些半编译状态),然后在虚拟"机器"上运行.


Car*_*icz 25

极端简单的案例:

  • 编译器将以目标机器的本机可执行格式生成二进制可执行文件.此二进制文件包含除系统库之外的所有必需资源; 它已准备好运行而无需进一步的准备和处理,它像闪电一样运行,因为代码是目标机器上CPU的本机代码.

  • 解释器将在循环中向用户显示提示,在该循环中他可以输入语句或代码,并且在敲击RUN或等效时,解释器将检查,扫描,解析和解释性地执行每一行,直到程序运行到停止点或错误.因为每一行都是自己处理的,并且解释器没有"学习"任何东西,因为之前已经看过该行,所以每次为每一行都会产生将人类可读语言转换为机器指令的努力,因此它的速度很慢.从好的方面来说,用户可以通过各种方式检查和以其他方式与他的程序交互:更改变量,更改代码,以跟踪或调试模式运行......等等.

有了这些,让我解释说生活不再那么简单了.例如,

  • 许多口译员会预编译他们给出的代码,因此翻译步骤不必一再重复.
  • 有些编译器不是编译CPU特定的机器指令,而是编译为字节码,这是一种用于虚构机器的人工机器代码.这使得编译的程序更加便携,但是在每个目标系统上都需要字节码解释器.
  • 字节码解释器(我在这里看Java)最近倾向于重新编译它们在执行之前为目标部分的CPU获得的字节码(称为JIT).为了节省时间,通常只对经常运行的代码(热点)执行此操作.
  • 一些看起来像解释器(例如Clojure)的系统会立即编译它们获得的任何代码,但允许交互式访问程序的环境.这基本上是具有二进制编译速度的解释器的便利性.
  • 有些编译器没有真正编译,它们只是预先消化和压缩代码.我听说过一段时间Perl的工作原理.所以有时编译器只是做了一些工作,而且大部分仍然是解释.

最后,现在,解释与编译是一种权衡,花费的时间(一次)编译通常会得到更好的运行时性能的奖励,但解释性环境会提供更多的交互机会.编译与解释主要是关于"理解"程序的工作如何在不同的流程之间划分的问题,而且现在这条线路有点模糊,因为语言和产品试图提供两全其美的优势.


Bha*_*hah 20

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

没有区别,因为"编译编程语言"和"解释编程语言"并不是有意义的概念.任何编程语言,我的意思是任何,都可以被解释或编译.因此,解释和编译是实现技术,而不是语言的属性.

解释是一种技术,其中另一个程序,即解释器,代表正在解释的程序执行操作以便运行它.如果你可以想象阅读一个程序并按照它所说的一步一步做,就像在一张草稿纸上说的那样,那就是翻译所做的.解释程序的一个常见原因是解释器相对容易编写.另一个原因是,解释器可以监视程序在运行时尝试执行的操作,以实施策略,例如安全性.

编译是一种技术,用一种语言("源语言")编写的程序被翻译成另一种语言的程序("对象语言"),这有望与原始程序相同.在进行转换时,编译器也常常尝试以使对象程序更快的方式转换程序(不改变其含义!).编译程序的一个常见原因是,有一些很好的方法可以快速运行目标语言中的程序,而且不会产生解释源语言的开销.

根据上述定义,您可能已经猜到这两种实现技术并不是相互排斥的,甚至可能是互补的.传统上,编译器的目标语言是机器代码或类似的东西,它指的是特定计算机CPU理解的任何数量的编程语言.然后机器代码将"在金属上"运行(尽管人们可能会看到,如果足够仔细地看,"金属"的工作方式很像解释器).然而,今天,使用编译器生成要解释的目标代码是很常见的 - 例如,这就是Java过去(有时仍然如此)工作的方式.有些编译器会将其他语言翻译成JavaScript,然后通常在Web浏览器中运行,这可能会解释JavaScript,或者将其编译为虚拟机或本机代码.我们还有机器代码的解释器,可用于在另一个上模拟一种硬件.或者,可以使用编译器生成目标代码,然后该代码是另一个编译器的源代码,它甚至可以及时编译内存中的代码以便运行,反过来...你明白了.有很多方法可以将这些概念结合起来.

  • 第一段非常翔实! (4认同)

Nik*_*lic 10

解释源代码优于已编译源代码的最大优点是可移植性.

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

如果您的源代码被解释,您只需要编写一次,它可以由任何平台上的相应解释器解释和执行!它便于携带!注意,解释器本身是一个可执行程序编写和编译为特定平台.

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


Mic*_*rdt 7

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

传统上,"编译"意味着这种翻译一次完成,由开发人员完成,并将生成的可执行文件分发给用户.纯粹的例子:C++.编译通常需要很长时间,并尝试进行大量昂贵的optmization,以便生成的可执行文件运行得更快.最终用户没有自己编译内容的工具和知识,并且可执行文件通常必须在各种硬件上运行,因此您无法进行许多特定于硬件的优化.在开发期间,单独的编译步骤意味着更长的反馈周期.

传统上,"解释"意味着当用户想要运行程序时,翻译"在运行中"发生.纯粹的例子:香草PHP.天真的解释器必须在每次运行时解析和翻译每段代码,这使得它非常慢.它不能进行复杂,昂贵的优化,因为它们花费的时间比执行时节省的时间长.但它可以充分利用其运行的硬件的功能.缺乏separrate编译步骤减少了开发期间的反馈时间.

但是现在"编译与解释"并不是一个黑白问题,中间有阴影.天真,简单的口译员已经灭绝了.许多语言使用两步过程,其中高级代码被转换为平台无关的字节码(解释速度快得多).然后你有"及时编译器",每次程序运行最多编译一次代码,有时会缓存结果,甚至智能地决定解释很少运行的代码,并对运行很多的代码进行强大的优化.在开发期间,调试器能够在正在运行的程序中切换代码,即使对于传统编译语言也是如此.


hac*_*ddy 7

我猜这是计算机科学中最大的误解之一。因为解释和编译是完全不同的两个东西,我们不能这样比较。

编译是将一种语言翻译成另一种语言的过程。编译的类型很少。

  • 编译 - 将高级语言翻译成机器/字节代码(例如:C/C++/Java)
  • 翻译 - 将高级语言翻译成另一种高级语言(例如:TypeScript)

解释是实际执行程序的过程。这可能以几种不同的方式发生。

  • 机器级解释 - 这种解释发生在编译成机器代码的代码上。指令由处理器直接解释。C/C++ 等编程语言生成可由处理器执行的机器代码。所以处理器可以直接执行这些指令。

  • 虚拟机级别解释 - 这种解释发生在未编译为机器级别(处理器支持)代码,而是编译为某些中间级别代码的代码。该执行是由另一个软件完成的,该软件由处理器执行。此时实际上处理器并没有看到我们的应用程序。它只是执行虚拟机,虚拟机正在执行我们的应用程序。Java、Python、C# 等编程语言生成字节码,可由虚拟解释器/机器执行。

所以最终我们必须明白的是,世界上所有的编程语言都应该在某个时候被解释。它可以由处理器(硬件)或虚拟机来完成。

编译只是将我们编写的人类可理解的高级代码提升到某种硬件/软件机器可理解的级别的过程。

这是完全不同的两个事情,我们无法比较。但这个术语对于教导初学者编程语言如何工作非常有用。

PS:
一些编程语言(例如 Java)有一种混合方法来执行此操作。首先,将高级代码编译为虚拟机可读的字节代码。一个称为 JIT 编译器的组件会即时将字节码编译为机器代码。具体来说,多次执行的代码行会被翻译成机器语言,这使得解释过程更快。因为硬件处理器总是比虚拟解释器/处理器快得多。

Java JIT 编译器的工作原理


Uri*_*Uri 5

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

更重要的是:我认为交互性是主要的实际区别。由于所有内容都被解释,您可以提取一小段代码,针对当前环境状态解析并运行它。因此,如果您已经执行了初始化变量的代码,您将可以访问该变量等。它确实适用于诸如函数式风格之类的事情。

然而,解释的成本很高,尤其是当您拥有一个包含大量参考资料和上下文的大型系统时。根据定义,这是一种浪费,因为相同的代码可能必须被解释和优化两次(尽管大多数运行时都为此进行了一些缓存和优化)。尽管如此,您仍需支付运行时成本,并且通常需要一个运行时环境。您也不太可能看到复杂的过程间优化,因为目前它们的性能不够交互。

因此,对于不会有太大变化的大型系统,对于某些语言,预编译和预链接所有内容,做所有可以做的优化更有意义。这最终会得到一个非常精简的运行时,该运行时已经针对目标机器进行了优化。

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

我不同意所有语言都可以交互。某些语言,例如 C,与机器和整个链接结构如此紧密地联系在一起,我不确定您是否可以构建一个有意义的成熟的交互式版本

  • GCJ *确实* 生成本地机器代码,而不仅仅是带有嵌入式解释器和字节码的可执行环境。libgcj 提供了一个字节码解释器来支持从本机代码调用到 Java 字节码,而不是解释编译后的程序。如果 libgcj 不提供字节码解释器,GCJ 将不符合 Java 规范。 (2认同)
  • [你](http://www.softintegration.com/) [可以](http://root.cern.ch/drupal/content/cint) [解释](http://www.artificialworlds.net/wiki /IGCC/IGCC) [C](http://www.programmersheaven.com/download/16526/download.aspx)。 (2认同)