C# 是部分解释还是真正编译?
Posted
技术标签:
【中文标题】C# 是部分解释还是真正编译?【英文标题】:Is C# partially interpreted or really compiled? 【发布时间】:2012-01-12 15:11:39 【问题描述】:关于这点有很多相互矛盾的信息。虽然有人说 C# 是编译的(因为它被编译成 IL,然后在运行时编译为本机代码),但其他人说它被解释为它需要 .NET。 EN Wiki 说:
许多解释型语言首先被编译成某种形式的虚拟 机器代码,然后在运行时被解释或编译 到本机代码。
所以我很困惑。谁能解释清楚?
【问题讨论】:
Is C# Interpreted or Compiled? 的可能重复项 相关帖子 - If C# is not interpreted, then why is a VM needed? inb4 有人发明了一种作为 LLVM IR 分发的语言,并要求用户安装一些独立的 LLVM 实例。 我知道这是旧的,但我认为它仍然相关。这里的大多数讨论来自“解释语言”与“编译语言”的不同定义。可以通过 (1) 澄清 OP 对这些术语的具体定义或 (2) 将“解释”与“编译”的问题从关于构建/运行 C# 代码的可用/标准的问题中分离出来来改进这个问题。对于那些对 C# .NET 的原生提前 (Native AOT) 编译感兴趣的人,请参阅this repo。 【参考方案1】:C# 被 c# 编译器编译成 IL。
然后,此 IL 会根据需要即时 (JIT) 编译成主机的本机汇编语言。不过,可以编写一个解释 IL 的 .NET 运行时。即使这样做了,我仍然认为 c# 是一种编译语言。
【讨论】:
但是为什么它被许多程序员认为是解释语言呢? 就我个人而言,我从未听说过有人将 C# 称为解释型语言。 也许他们只是弄错了,或者被误导了。或者也许我错了:-) JIT 编译!= 解释 @JoSmo csc.exe 是构建 c# 的命令行工具。它代表 C-Sharp 编译器【参考方案2】:纯编译语言有一些优点。速度,通常,通常是工作集大小。 纯解释型语言有一些优点。不需要显式编译阶段的灵活性,允许我们在适当的位置进行编辑,并且通常更容易移植。
在这种情况下,jitted 语言适合中间立场。
这就是为什么我们可能会认为 jitted 语言是编译的还是解释的,这取决于我们关心达到哪个指标的位置,以及我们对一个或另一个的偏见。
C# 也可以在第一次运行时编译,就像在 ASP.NET 中发生的那样,这使得它在这种情况下接近于解释(尽管在这种情况下它仍然编译为 IL 然后 jitted)。当然,在这种情况下,它几乎具有解释的所有优点(与经典 ASP 中使用的 VBScript 或 JScript 相比),以及编译的许多优点。
严格来说,没有一种语言是 jitted、解释或编译的 qua 语言。我们可以将 NGen C# 转换为本机代码(尽管如果它执行诸如动态加载程序集之类的操作,它仍将使用 IL 和 jitting)。我们可以为 C 或 C++ 编写一个解释器(有几个人已经这样做了)。不过,在其最常见的用例中,C# 被编译为 IL,然后进行 jitted,这不是解释或编译的经典定义。
【讨论】:
我同意no language is jitted, interpretted or compiled qua language
。我看到了一些关于 python 的信息,它首先被编译为byteCode
,然后在运行时byteCode
被相应操作系统的解释器解释。这个事实只会增加我对编译、jit 和解释语言的定义所引起的惊奇。
JIT 语言可以比原生语言更快。您知道 gcc 具有的检测功能,它在运行时输出性能测量文件,然后将其反馈给 gcc 以编译更优化的二进制文件? JVM 和 CLR 都在不断地这样做。【参考方案3】:
基于意见的语义和陈述过多。
首先:C# 不是解释型语言; CLR 和 JVM 被认为是“运行时”或“中间件”,但同样的名称也适用于 Perl 之类的东西。这在关心名字的人中造成了很多混乱。
引用运行时的术语“解释器”通常意味着现有代码解释一些非本地代码。有两大范式:解析读取原始源代码并采取逻辑动作;字节码执行首先将代码编译为非本地二进制表示,这需要更少的 CPU 周期来解释。
Java 最初编译为字节码,然后经过解释器;现在,JVM 读取字节码并及时将其编译为本机代码。 CIL 也是如此:CLR 使用即时编译来编译本机代码。
考虑运行源代码、运行字节码、编译为本机、即时编译、通过编译器运行源代码到即时本机等的所有组合。一种语言是编译还是解释的语义变得毫无意义。
例如:许多解释语言使用即时字节码编译。 C#编译成CIL,JIT编译成native;相比之下,Perl 立即将脚本编译为字节码,然后通过解释器运行该字节码。您只能以 CIL 字节码格式运行 C# 程序集;您只能以原始源代码格式运行 Perl 脚本。
即时编译器还运行大量外部和内部工具。运行时跟踪各种函数的执行,然后调整代码布局以优化其特定执行流程的分支和代码组织。这意味着 JIT 代码可以比本机编译的代码运行得更快(通常像 C++ 那样,或者像通过 IL2CPP 运行的 C#),因为 JIT 在运行时会根据代码的实际执行情况调整其优化策略。
欢迎来到计算机编程的世界。我们决定让它变得非常复杂,然后给所有东西附加非描述性的名字。目的是在没有实际意义的词的定义上引发激烈的争论。
【讨论】:
JVM 解释字节码,然后将一些方法/循环体编译为本机代码。这种编译可以与字节码解释并行进行,并且可以热交换代码。 我找不到相关的引用。我的理解是 JVM 在 JIT 运行时解释字节码——也就是说:当它在当前运行期间遇到尚未处理的字节码时,它会通过 JIT 运行它并执行生成的本机代码块。 JIT-and-execute 比字节码解释更便宜,而且实现这两个系统都非常复杂。 结帐例如slideshare.net/ZeroTurnaround/…。您所描述的是.NET 所做的。我所描述的是例如Hotspot JVM 确实如此,是的,它非常复杂,这就是它被开发了一段时间的原因。您需要对代码进行字节码解释以获取配置文件信息(想想:大部分时间都采用了哪个 if 分支,等等),这比优化编译器中使用的要多(这个 if 分支从未被采用,不要花费太多优化它的资源)。 其实perl现在也被jit了。 您可以将分析检测到本机代码中。 gcc 有这样的分析工具。编译器可以识别一个循环迭代器并添加代码来比较它的进入和退出状态;它可以在分支时设置一个变量;等等。这可以计算每次 JIT 检查分析数据之间的调用次数、循环计数、分支计数等。它需要几个周期——几乎没有解释字节码那么多。【参考方案4】:如果您觉得、了解或老派,编译的 EXE 正在从源代码转换为机器代码,那么 C# 将被解释。 如果您认为已编译意味着将源代码转换为其他代码(例如字节码),那么是的,它已转换。对我来说,任何需要运行时处理才能在为其构建的操作系统中工作的东西都会被解释。
【讨论】:
【参考方案5】:看这里:http://msdn.microsoft.com/library/z1zx9t92
用 C# 编写的源代码被编译成中间语言 (IL) 符合 CLI 规范。
(...)
当C#程序执行时,程序集被加载到CLR中, 它可能会根据信息中的信息采取各种行动 显现。然后,如果满足安全要求,CLR 执行 即时 (JIT) 编译将 IL 代码转换为本机代码 机器指令。
【讨论】:
谢谢,所以如果我理解正确,与 intepretes 的混淆可能来自所需的虚拟机 (CRM),但它不是真正的 intepreter。 链接已关闭 ;-)【参考方案6】:首先让我们了解解释和编译的定义。
"Compile" (when referring to code) 表示将代码从一种语言翻译成另一种语言。通常从人类可读的源代码到目标处理器可以处理的机器代码。
"Interpret" (when referring to code) ALSO 表示将代码从一种语言翻译成另一种语言。但这一次,它通常用于从人类可读的源代码转换为中间代码,由虚拟机获取,虚拟机将其解释为机器代码。
说清楚 源代码 -> 编译器 -> 机器码 源代码 -> 编译器 -> 字节码 -> 解释器 -> 机器码
理论上,任何语言都可以是interpreted 或compiled。通常,Java 被编译成字节码,由 Java 虚拟机解释成机器码。 C# 通常被解释为由 CLR、公共语言运行时、另一个虚拟机编译的字节码。
到目前为止,这一切都是营销噱头。添加了“解释”一词(或至少增加了使用量)以帮助展示just-in-time compiling 的简洁程度。但他们本可以只使用“编译”。区别更多的是对英语语言和商业趋势的研究,而不是任何技术性质的东西。
【讨论】:
在解释器中,运行时不生成机器码,只执行现有的机器码。按照这个标准,将公共语言运行时称为解释器是完全错误的。【参考方案7】:C# 在其生命周期内都被解释和编译。 C# 被编译为由 VM 解释的虚拟语言。
混淆源于“编译语言”的模糊概念。
“编译语言”在某种意义上是用词不当,因为编译或解释不是语言的属性,而是运行时的属性。
例如您可以编写 C 解释器,但人们通常称其为“编译语言”,因为 C 实现编译为机器代码,并且该语言的设计考虑了编译。
【讨论】:
不,桌面 .NET VM(称为公共语言运行时)有一个 JIT 编译器,没有解释器。相比之下,在甚至可能不支持运行时代码修改的微型嵌入式设备上发现的 .NET 微框架实际上可以解释 IL。 源代码->(编译器)->中间语言->(解释器)->本机代码。我将 JIT 视为解释器优化。它基本上是在解释器层“缓存”。如果你愿意,你可以调用这两个步骤编译器。【参考方案8】:大多数语言(如果不是全部)都需要一个解释器,将它们的脚本翻译成机器代码,以便让 cpu 理解和执行它!
每种语言处理翻译过程的方式不同!
例如,我们可以将“AutoIt”描述为 100% 解释性语言!
为什么?
因为“AutoIt”解释器在其脚本执行时经常需要!请参见下面的示例:
Loop, 1000
Any-Code
“AutoIt”解释器必须将“Any-Code”翻译成机器代码 1000 次,这会自动使“AutoIt”成为一种慢速语言!
另一方面,C# 处理翻译过程的方式不同,C# 的解释器只需要一次,在脚本执行之前,之后在脚本执行期间不再需要!
C# 的解释器只需将“Any-Code”一次翻译成机器代码,这会自动使“C#”成为一种快速语言!
基本上,
在脚本执行期间需要其解释器的语言是“解释语言”!
只需要一次解释器(在脚本执行之前)的语言是“编译语言”!
最后,
“AutoIt”是一种“解释语言”!
“C#”是“编译语言”!
【讨论】:
【参考方案9】:我相信这是一个相当古老的话题。
在我看来,解释代码会通过解释器,逐行翻译并同时执行。和示例javascript一样,它是一个解释代码,当一行javascript遇到错误时,脚本就会中断。
在编译代码时,它会经过编译器,将所有代码一次转换为另一种形式的代码,而无需先执行。执行是在另一个上下文中。
【讨论】:
JavaScript 要么像 C# 一样立即进行 JIT 编译,要么像 HotSpot JVM 一样执行,直到部分代码变热,此时它通过优化编译器进行 JIT 编译。这取决于 JavaScript 引擎。 JavaScript 已经有 10 多年没有被解释过了。【参考方案10】:C# 与 Java 一样,具有混合语言处理器。混合处理器执行解释和编译工作。
【讨论】:
【参考方案11】:由于计算机只能执行二进制代码,因此任何语言都会在某一时刻导致生成二进制代码。 问题是:该语言是否允许您以二进制代码生成程序? 如果是,那么它是一种编译语言:根据定义,“编译语言”中的“编译”是指编译成二进制代码,而不是转换成一些中间代码。 如果该语言导致为程序生成此类中间代码,则需要额外的软件来从该代码执行二进制编译:它是一种解释性语言。 由C#“编译”的程序是否可以在一台机器上直接执行,而这台机器上根本没有安装任何其他软件?如果不是,那么它是一种解释语言。 对于解释型语言,它是一个解释器,它将生成底层二进制代码,大多数时候以动态方式生成,因为这种机制是此类语言灵活性的基础。 雷姆。 : 有时它看起来并不明显,因为解释器被捆绑到操作系统中
【讨论】:
根据这个定义,QBasic 是一个编译器。这分明是个缸。 C# 也是一种解释型语言,这里和那里都有一些 JITting 作为优化。当然,您也可以使用 NGen .Net IL,但是您失去了 JITter 可以做的优化。您在 IL 中获得的是用于将调用分派到子例程中的数据列表,不会生成或执行机器代码,除非运行时看到大量使用并用 JITted 机器代码替换 p 代码数据的运行以替换调用分派。 【参考方案12】:如果我们同意解释器的定义 «在计算机科学中,解释器是直接执行(即执行)用编程或脚本语言编写的指令的计算机程序,而无需事先将它们编译成机器语言程序。» 毫无疑问:C# 不是解释性语言。
Interpreter on Wikipedia
【讨论】:
【参考方案13】:C# 是可编译的语言。
可能我也遇到过这样的意见,有人认为有C#语言的解释器,是因为像这样的项目
C# Interpreter Console
或者,例如,著名的
LinqPAD
您可以只编写几行代码并执行它们,这让人觉得这是Python 之类的语言,这是不正确。它编译这些行并执行它们,就像普通的可编译编程语言一样(从工作流的角度来看)。
【讨论】:
谢谢。但是,从这个角度来看,C++ 和 C# 都将是编译语言。但我认为它不像 C++ 直接编译为本机代码那样清楚,而 .NET 只是 CIL。 C#,正如这里所回答的,首先被编译成中间语言,并且只有在编译成机器特定的机器代码之后。有一个原因检查 Eric Lippert 的回答 ***.com/questions/7875253/… 以了解其背后的原因。以上是关于C# 是部分解释还是真正编译?的主要内容,如果未能解决你的问题,请参考以下文章