在我的理解中,编译器获取程序的源代码,例如,
A = 2;
B = 3;
print(2 + 3);
编译它,在输出我们得到机器码。假设,有条件地,结果是 3 个命令:
1110
1101
1010
此外,我们不再需要编译器,我们可以将编译后的代码传输到任何地方(同一个处理器的地方)并且它会成功执行。
解释器会依次处理每一行,将其转换为其虚拟机的中间字节码,然后虚拟机将其命令依次翻译成机器码,即处理器命令
问题:最终解释器会执行相同的命令吗?
1110 1101 1010如果是这样,为什么永远不可能得到解释语言的编译代码?
另外,当他们谈论编译器时,他们使用编译器执行整个程序的短语,这是什么意思?毕竟,处理器还是会依次执行机器代码,一次一条指令?也就是说,到底什么是编译后的代码,解释后的代码在发送到处理器的一个命令上会执行什么?
第一种说法并不完全正确——尽管解释器执行的程序实际上可以和编译后的程序做同样的事情,但粗略地说,指令会出现在编译程序的指令之间,服务于解释器的内部工作.
通常解释器一次通过一个表达式,解析这个表达式并立即执行它。根本没有代码生成。还有一个过渡选项——这是 JIT 编译(js、java),这已经是纯解释器(BASIC)和纯编译器(C++)之间的中间情况
第二个子问题——有时你可以,如果你缩小可能的行动的数量。例如,KPHP允许您将 PHP 程序转换为 C++ 代码。
为什么不能总是编译翻译语言的程序(许多语言都有)的最有力的例子
eval()是执行该语言的字符串的函数。因此,有必要在编译后的程序中实现整个解释器。此外,从编译的程序中删除了很多东西——变量名、函数名、类型信息。有时,编写为带有方法的类的内容被吹成几条机器指令 - 因此从解释语言编译程序没有用 - 优化(为此您必须忍受 C++ 的不便)变得不可能。
我从来没有听说过编译器会做一些“整体”的事情。编译器遍历程序(有时不止一次),然后立即为整个翻译单元创建二进制代码。
考虑一下当你给编译器一个高级编程语言的程序作为输入的情况。它会给你一个汇编语言的输出程序。以可执行文件、目标文件的形式,或者它可以简单地在屏幕上显示汇编代码。没关系,因为编译器的任务只是将程序从高级语言翻译成汇编语言。这就像一个从俄语到英语的翻译器。
如果你给解释器一个高级编程语言的程序作为输入,解释器不会给你任何其他输出程序,它会执行你传递给它的程序。他怎么做并不重要。他可以用它为所欲为:他可以将其翻译成字节码并在虚拟机上执行;无法在任何地方翻译任何内容并立即转到 AST *并执行它;可以翻译成本机代码(汇编程序)并已经执行。这是最后一种情况,当解释器将代码翻译成处理器的本机指令时,称为JIT 编译‡。
可以看出,编译器并没有执行程序,它只是将它从一种语言翻译成另一种语言。但是解释器执行程序,他是否将其翻译成某种东西并不重要,这是他自己的事。
关于你的问题:
没有必要,主要的是观察到的程序行为保持不变。你可以编写不同的指令,最终做同样的事情。
为什么不,为什么不。例如,您可以获取JavaScript 代码的 JIT 编译结果。
“*...他们使用这样的短语,编译器执行整个程序... ”,我从未听说过,您自己现在知道编译器什么都不做。“即 最后,解释的编译代码将在发送到处理器的 1 个命令上执行?仅在解释程序的情况下,这不是直接发生的,而是通过中介 - 解释器发生的。
例如,让我们编写一个简单的语言解释器,它将具有三个运算符:递增计数器
+、递减计数器-和输出计数器@。整个程序就是这三个字符的字符串。这就是这种语言的解释器的样子:
请注意,没有从我们的语言到汇编语言的翻译。当我们的解释器遇到一个 operator
+时,它只是调用一些函数来做一些事情,在这种情况下增加一个计数器。也就是说,解释器理解我们想要得到的东西,没有将任何东西翻译成汇编程序,只是做了所有需要的事情。
*这称为 tree-walk 解释。
†好吧,如果您不考虑各种微架构细节,例如指令级别的并行性。
‡例如,有 HotSpot JVM。在执行程序时,它会在程序代码中找到这些经常执行且占用大量处理器时间的地方,并将它们编译成本机处理器指令,以便这些地方(它们称为热点)运行得更快。这是 JIT 编译技术的一个示例,即动态编译。