我最近阅读了编译器设计的龙书。它提到编译器将中间代码生成作为其产生机器无关代码的阶段之一。那么为什么C不是像java那样开发为平台无关的语言?
答案 0 :(得分:9)
Dragon Book描述的是以下过程:
这样做的好处是,如果您想支持其他系统,只需在步骤3中添加新的代码生成器,而无需触及步骤1和2.
所有常见的C编译器都以这种方式工作。因此,如果您的问题是"为什么不让C编译器执行Dragon Book所描述的内容?",答案是:"他们做"。
现在你提到了Java。 Java编译器的作用如下:
现在要运行这个字节代码,你需要一个JVM,它解释字节代码和/或JIT编译它。优化和分析通常在JIT编译期间发生。这不是龙书中描述的过程。
来自语言实施者'从观点来看,这并没有改变支持新目标系统的努力。您不再需要更改编译器,而是必须更改JVM:而不是必须向javac编译器添加新的后端,而是向JIT编译器添加新的后端。努力基本保持不变。
主要区别在于Java程序员:您现在可以编译代码并将结果包提供给每个人,而不是为每个目标平台编译程序并为每个平台分发程序包。现在运行代码的人需要安装JVM才能使用该软件包,因此您基本上将工作从程序员转移到最终用户,但是安装JVM只需要执行一次(不是每个Java都需要)你要运行的程序。)
因此,而不是"写一次,到处编译",你现在已经"编译一次,到处运行"。
那么为什么C不做与Java相同的事情呢?性能。解释字节代码很慢(与运行编译代码相比),JIT编译会导致启动时间增加。
答案 1 :(得分:2)
C最初是针对特定用例而设计的,涉及特定的机器。虽然它松散地基于通过独立于平台的虚拟机实现的语言BCPL,但C的目标是能够编写低级代码,例如操作系统,这意味着它需要能够利用目标机器的特定功能,特别是它能够直接处理单个字节。相比之下,BCPL的底层架构坚决以文字为导向。
贝尔实验室能够以新语言(C)迅速重新实现Unix操作系统的事实无疑使其受欢迎。 (至少,这就是我最初学习它的原因。)为了允许更广泛地传播语言,编译器的一个版本更紧密地遵循龙书中概述的体系结构,初始生成的虚拟机代码是然后用于为目标机器生成代码。这个Portable C Compiler多年来一直是参考实现,并且仍然可用。
其他当代C语言,特别是Pascal,也使用了针对平台独立的vurtual机器的策略,曾经常见的是将虚拟机代码称为“P-Code”,因为这就是Niklaus Wirth的Pascal项目所谓的目标架构。
尽管GCC不使用虚拟机,但它确实从生成与机器无关的内部表示开始,简化了将编译器移植到新的archutectures的任务。当然,Clang编译器生成LLVM(低级虚拟机)代码,可以将其转换为各种具体的机器代码,或直接解释。
答案 2 :(得分:1)
简短回答:因为当时不可行。
答案很长:Java平台是一种语言+虚拟机,Java代码编译成一个名为ByteCode的东西,然后虚拟机可以获取这个字节代码(它类似于汇编语言)并将其转换为相关命令在运行时,意味着本地机器将理解的机器指令。
每个架构都有自己的指令集,这意味着ARM架构将无法理解为x86架构编译的代码。
在C中,c代码直接编译为机器指令,然后由本地机器执行这些指令。 为了得到像Java这样的行为,你需要有一种读取C并在运行时将其转换为机器代码的解释器,这不是一项廉价的任务,对于当时的计算机而言太过分了(c于1972年发明)当然,另一种可以实现的方法是让用户在使用它之前编译你的程序,这可能很好,但可能会让你的源代码对客户端可见,这是不需要的。 希望能澄清一些事情。
答案 3 :(得分:1)
C最初设计和编写为"一次写入,编译随处可见"语言,它与当时通用语言一样接近。
处理器和架构是如此根本不同,资源非常小,以至于通用虚拟机(如Java)的想法是不可能的。
单个代码库可以通过编译器运行,然后在任何目标平台上拥有相同的软件的想法是非常不可思议的。
答案 4 :(得分:0)
除了留下一些实现定义的(实际上这主要是平台/ ABI定义的,但严格来说不是必须的),C 是主要是与平台无关的语言。实际上,存在C(例如emscripten)的实现,其以可以在具有适当运行时环境的任何机器平台上运行的形式产生输出。如果用C语言编写的软件对语言的实现定义(或更糟,未定义)方面做出假设,那么它可能无法在某些实现/机器上工作,但通常原因更多的是API /环境/库假设(如假设POSIX,或Windows,或glibcisms),而不是对语言本身做出不可移植的假设。