为什么C语言不能成为平台无关语言?

为什么C语言不能成为平台无关语言?,c,compiler-construction,platform-independent,C,Compiler Construction,Platform Independent,我最近读了编译器设计的龙之书。它提到编译器将中间代码生成作为其生成独立于机器的代码的阶段之一。那么为什么C语言没有像java那样被开发成独立于平台的语言呢?简单的回答是:因为它在当时是不可行的 答案很长:Java平台是一种语言+虚拟机,Java代码编译成一种叫做字节码的东西,然后虚拟机可以将这个字节码(类似于汇编语言)转换成运行时的相关命令,这意味着本地机器可以理解的机器指令 每个体系结构都有自己的指令集,这意味着ARM体系结构将无法理解为x86体系结构编译的代码 在C语言中,C代码直接编译成机

我最近读了编译器设计的龙之书。它提到编译器将中间代码生成作为其生成独立于机器的代码的阶段之一。那么为什么C语言没有像java那样被开发成独立于平台的语言呢?

简单的回答是:因为它在当时是不可行的

答案很长:Java平台是一种语言+虚拟机,Java代码编译成一种叫做字节码的东西,然后虚拟机可以将这个字节码(类似于汇编语言)转换成运行时的相关命令,这意味着本地机器可以理解的机器指令

每个体系结构都有自己的指令集,这意味着ARM体系结构将无法理解为x86体系结构编译的代码

在C语言中,C代码直接编译成机器指令,然后这些指令由本地机器执行。 要获得像Java这样的行为,你需要某种解释器来读取C并在运行时将其转换为机器代码,这不是一项廉价的任务,对于1972年发明C时的计算机来说太多了。当然,实现这一点的另一种方法是让用户在使用它之前编译你的程序,这可能很好,但可能涉及到让客户机看到您的源代码,这是不需要的。
希望这能澄清一些问题。

简短的回答是:因为这在当时是不可行的

答案很长:Java平台是一种语言+虚拟机,Java代码编译成一种叫做字节码的东西,然后虚拟机可以将这个字节码(类似于汇编语言)转换成运行时的相关命令,这意味着本地机器可以理解的机器指令

每个体系结构都有自己的指令集,这意味着ARM体系结构将无法理解为x86体系结构编译的代码

在C语言中,C代码直接编译成机器指令,然后这些指令由本地机器执行。 要获得像Java这样的行为,你需要某种解释器来读取C并在运行时将其转换为机器代码,这不是一项廉价的任务,对于1972年发明C时的计算机来说太多了。当然,实现这一点的另一种方法是让用户在使用它之前编译你的程序,这可能很好,但可能涉及到让客户机看到您的源代码,这是不需要的。
希望这能澄清一些问题。

除了在实践中留下一些实现定义之外,这主要是由平台/ABI定义的,但严格来说不一定是,C语言主要是一种平台无关的语言。事实上,有一些C语言的实现,比如emscripten,它们以某种形式生成输出,可以在任何具有适当运行时环境的机器平台上运行。如果用C编写的软件对语言的实现定义或更糟的未定义方面做出假设,那么它可能无法在某些实现/机器上工作,但原因往往更多地是API/环境/库假设的问题,如假设POSIX或Windows,或glibcism,而不是对语言本身做出不可移植的假设。

除了在实践中留下一些实现定义之外,这主要是由平台/ABI定义的,但严格来说不一定是,C语言基本上是一种独立于平台的语言。事实上,有一些C语言的实现,比如emscripten,它们以某种形式生成输出,可以在任何具有适当运行时环境的机器平台上运行。如果用C编写的软件对语言的实现定义或更糟的未定义方面做出假设,那么它可能无法在某些实现/机器上工作,但原因往往更多地是API/环境/库假设的问题,如假设POSIX或Windows,C最初是作为一种写一次,编译一次的语言而设计和编写的,这是当时最接近通用语言的语言

处理器和体系结构完全不同,资源又如此之少,以至于像Java这样的通用虚拟机的想法是不可能的


一个单一的代码库可以通过一个编译器运行,然后在任何目标平台上都有相同的软件,这种想法是非常令人难以置信的。

C最初设计并编写为一次编写、随处编译的语言,这是当时最接近通用语言的语言

处理器和体系结构完全不同,而资源又如此之小,以至于像Java这样的通用虚拟机的想法已经过时了 这是不可能的


一个代码库可以通过一个编译器运行,然后在任何目标平台上都有相同的软件,这一想法非常令人难以置信。

C最初是为一个特定的用例设计的,它涉及一台特定的机器。虽然它松散地基于语言BCPL,该语言是通过独立于平台的虚拟机实现的,但C的目标是能够编写底层代码,如操作系统,这意味着它需要能够利用目标机器的特定功能,特别是它直接寻址单个字节的能力。相比之下,BCPL的底层架构完全是面向文字的

贝尔实验室能够用他们的新语言C快速重新实现Unix操作系统,这一事实无疑促进了它的普及。至少,这就是我最初学习它的原因。为了更广泛地传播该语言,编译器的一个版本是按照《龙之书》中概述的体系结构编写的,最初生成了虚拟机代码,然后用于为目标机器生成代码。这个可移植的C编译器多年来一直是一个参考实现,现在仍然可以使用

与C语言同时代的其他语言,尤其是Pascal,也使用了针对独立于平台的虚拟机的策略,并且曾经将虚拟机代码称为p代码很常见,因为Niklaus Wirth的Pascal项目将其称为目标体系结构


尽管GCC本身并不使用虚拟机,但它确实是从生成独立于liw级机器的内部表示开始的,从而简化了将编译器移植到新体系结构的任务。当然,Clang编译器会生成LLVM低级虚拟机代码,这些代码可以转换成各种具体的机器代码,也可以直接进行解释。

C最初是为涉及特定机器的特定用例而设计的。虽然它松散地基于语言BCPL,该语言是通过独立于平台的虚拟机实现的,但C的目标是能够编写底层代码,如操作系统,这意味着它需要能够利用目标机器的特定功能,特别是它直接寻址单个字节的能力。相比之下,BCPL的底层架构完全是面向文字的

贝尔实验室能够用他们的新语言C快速重新实现Unix操作系统,这一事实无疑促进了它的普及。至少,这就是我最初学习它的原因。为了更广泛地传播该语言,编译器的一个版本是按照《龙之书》中概述的体系结构编写的,最初生成了虚拟机代码,然后用于为目标机器生成代码。这个可移植的C编译器多年来一直是一个参考实现,现在仍然可以使用

与C语言同时代的其他语言,尤其是Pascal,也使用了针对独立于平台的虚拟机的策略,并且曾经将虚拟机代码称为p代码很常见,因为Niklaus Wirth的Pascal项目将其称为目标体系结构


尽管GCC本身并不使用虚拟机,但它确实是从生成独立于liw级机器的内部表示开始的,从而简化了将编译器移植到新体系结构的任务。当然,Clang编译器会生成LLVM低级虚拟机代码,这些代码可以转换成各种具体的机器代码,也可以直接进行解释。

龙书描述的是以下过程:

将源代码编译成与中间机器无关的字节码格式 对该IR执行优化和分析 将IR转换为目标平台的实际机器代码 这样做的好处是,如果您想支持其他系统,只需为步骤3添加一个新的代码生成器,而不必触碰步骤1和2

所有常见的C编译器都是这样工作的。因此,如果你的问题是为什么C编译器不按照《龙之书》所描述的那样做,答案是:它们确实这样做了

现在你提到了Java。Java编译器的作用如下:

将Java代码编译成Java字节码。就Java编译器而言,这不是一种中间格式,而是实际的目标语言。 结局 现在要运行这个字节码,您需要一个JVM,它解释字节码和/或JIT编译字节码。优化和分析通常发生在JIT编译期间。这不是龙书中描述的过程

从语言实现者的角度来看,这并没有很大程度上改变支持新目标系统的努力。您不再需要更改编译器,而是必须更改JVM:而不必添加新的backe 对于javac编译器,您可以向JIT编译器添加一个新的后端。努力基本上保持不变

对于Java程序员来说,主要的区别在于:不再为每个目标平台编译程序并为每个平台分发包,现在您可以只编译一次代码并将生成的包提供给每个人。现在,运行代码的人需要安装一个JVM才能使用该包,因此基本上您将工作从程序员转移到了最终用户,但是安装JVM只需要一次,而不是对每个要运行的Java程序

因此,不再是一次编写,到处编译,而是编译一次,到处运行


那么为什么C没有做和Java一样的事情呢?表演与运行编译后的代码相比,解释字节代码的速度较慢,JIT编译会增加启动时间。

龙书描述的是以下过程:

将源代码编译成与中间机器无关的字节码格式 对该IR执行优化和分析 将IR转换为目标平台的实际机器代码 这样做的好处是,如果您想支持其他系统,只需为步骤3添加一个新的代码生成器,而不必触碰步骤1和2

所有常见的C编译器都是这样工作的。因此,如果你的问题是为什么C编译器不按照《龙之书》所描述的那样做,答案是:它们确实这样做了

现在你提到了Java。Java编译器的作用如下:

将Java代码编译成Java字节码。就Java编译器而言,这不是一种中间格式,而是实际的目标语言。 结局 现在要运行这个字节码,您需要一个JVM,它解释字节码和/或JIT编译字节码。优化和分析通常发生在JIT编译期间。这不是龙书中描述的过程

从语言实现者的角度来看,这并没有很大程度上改变支持新目标系统的努力。您不再需要更改编译器,而是必须更改JVM:您不必向javac编译器添加新的后端,而是向JIT编译器添加新的后端。努力基本上保持不变

对于Java程序员来说,主要的区别在于:不再为每个目标平台编译程序并为每个平台分发包,现在您可以只编译一次代码并将生成的包提供给每个人。现在,运行代码的人需要安装一个JVM才能使用该包,因此基本上您将工作从程序员转移到了最终用户,但是安装JVM只需要一次,而不是对每个要运行的Java程序

因此,不再是一次编写,到处编译,而是编译一次,到处运行


那么为什么C没有做和Java一样的事情呢?表演与运行编译后的代码相比,解释字节代码的速度较慢,而JIT编译会增加启动时间。

它的设计者并没有寻找像Java这样与平台无关的语言。他们想要比汇编程序更接近机器、更高级、更方便的东西,但仍然让他们编写紧凑的代码,运行效率与汇编程序相当——并使用20世纪70年代的编译器技术,作为一个小公司,保持简单的项目。独立于机器的代码可能意味着独立于机器的内部表示,但在该表示中仍然会有编码的依赖于机器的元素。话虽如此,我们还不清楚你到底在问什么。大部分情况下是这样的,除非你用它做一些愚蠢的事情。C的设计目的是让Unix内核能够用比汇编语言更高级的语言编写。早在70年代早期,用高级语言编写o/s内核在当时是一个鲁莽的步骤。从那时起,情况发生了巨大的变化@苏拉贾因可能是:-它的设计者并不是在寻找一种与平台无关的语言,比如Java。他们想要比汇编程序更接近机器、更高级、更方便的东西,但仍然让他们编写紧凑的代码,运行效率与汇编程序相当——并使用20世纪70年代的编译器技术,作为一个小公司,保持简单的项目。独立于机器的代码可能意味着独立于机器的内部表示,但在该表示中仍然会有编码的依赖于机器的元素。话虽如此,我们还不清楚你到底在问什么。大部分情况下是这样的,除非你用它做一些愚蠢的事情。C的设计目的是让Unix内核能够用比汇编语言更高级的语言编写。早在70年代早期,用高级语言编写o/s内核在当时是一个鲁莽的步骤。从那时起,情况发生了巨大的变化@苏拉贾因可能是:-例如,P代码的存在是否与那些假定为na的概念的不可置信性相矛盾
ive时代?例如,P代码的存在是否与那些被认为是幼稚的时代的概念的不可置信性相矛盾?我认为这就是答案,在我看来,OP对机器独立IR感到困惑!=平台无关语言。@ArturKink,更不用说在C编译器的上下文中没有机器无关的IR。GIMPLE和LLVM IR都严重依赖于平台。我认为这就是答案,在我看来OP对独立于机器的IR感到困惑!=平台无关语言。@ArturKink,更不用说在C编译器的上下文中没有机器无关的IR。GIMPLE和LLVM IR都严重依赖于平台。