为什么说c,c++不能跨平台,编译器是在计算机操作系统上的吗,难道说编译器不在c,c++程序里吗?

曾传新


首先我们纠正一下题目中一个小小的误区,不是C和C++不能跨平台,应该是说是C/C++源代码在编译后生成的

.exe

文件不能跨平台,源代码和可执行文件要区分开来。

想要搞明白这个问题,我们先得了解一下源代码是怎么变成程序的。

四个过程:预处理——编译——汇编——可执行文件

当我们编写完代码后,源代码会经过上述的四个环节,最终变成常见的可执行文件。

  1. 预处理阶段(hello.i):在源代码中会有头文件,一些宏,注释等。预处理的目的就是将头文件展开,宏文件代换,去掉注释等,对代码进行一些初步的处理

  2. 编译阶段(hello.s):这一阶段主要是检查语法上的错误,比如内存有没有溢出,指针有没有指错对象,然后生成可汇编文件。

  3. 汇编阶段(hello.o):计算机是不认识代码的,所以需要将汇编代码转换成0和1组成的机器码

  4. 链接(a.out):链接有两种情况:静态和动态。静态库和应用程序编译在一起,在任何情况下都能运行;而动态库是动态链接,文件生效时才会调用。最终生成一个可执行文件。

编译器的作用

我们不要把编译器想的太厉害,觉得编译器是万能的。实际上编译器就像是一个翻译,负责把高级语言转变成机器能看懂的低级语言,翻译过程就是上述的四个过程。但是其中有一点需要格外注意。那就是不同的公司使用的指令集不同。输出程序的格式和CPU使用的指令集有关,比如X86,arm,还有MIPS等等, 由于设计思路的差异,所以不同平台上编译生成后的可执行文件格式是不一样的,可能在ubantu里能运行的C程序,放到windows下就会报错。

类比一下java,为什么说java可以跨平台,是因为java内置了一个虚拟机,程序都从虚拟机中跑的,所以有人说“java不仅是一种语言,更是一个平台”。


综上所述,C/C++的一些基础性代码是可以跨平台的(可能会受API影响),是生成的可执行文件不能跨平台,C/C++不自带编译器,不同平台下的编译器存在差异。

(都看到最后了,麻烦点个赞和关注吧,谢谢~)


爱思考的奥特曼


先来讨论一下C&C++语言的执行过程,从而搞清楚为什么C&C++语言不能跨平台。

我们分阶段来讨论(如图4所示):

  • 预处理阶段。预处理器(cpp)来把 代码中开头的行进行展开, 比如头文件,宏等内容,修改最初的C文件。
  • 编译阶段。编译器(ccl)将修改后的C文件,翻译成了 另一文本文件,,这就是我们所说的汇编程序了。

  • 汇编阶段。汇编器(as)将翻译成机器语言指令。 把这些命令打包成一种叫做可重定位目标程序(relocatable object program)的格式,此时的输出格式就是了。这其实就是二进制文件了。
  • 链接阶段。编译过程最后还有一个链接阶段(程序调用了 函数),最后的输出结果还是和上一步类似,都是直接二进制文件。
图2以上就是大家都知道的程序。最终执行的结果 就是在console上输出一行字符串, ,如图3,4所示,大家可以很清楚的看到X86和ARM不同的编译结果。而在他们输入的0和1中,有些代表的是指令,这些是有固定含义和编码的。也是芯片能识别的。而另一些是数据。这些不同的程序的数据自然是不同的。我们前面就说,不管多么复杂的计算机操作,到了cpu级别都是0和1,数据虽然多变,但是 指令的数量是有限的。因为 指令是要被芯片固定识别的。芯片中要用 晶体管(最初是电子管)组成的与或非门组合来识别这些指令和数据。因为直接输入0和1,实在太繁琐了,所以他们就发明了汇编语言。来简化 程序的编写。

比如 计算 1+1,两个 数据1都 使用 来表示,而 加操作,放在cpu中,可以是 (这个是胡乱写的),这个二进制代表的加操作能被计算机识别。而因为这个加操作对于cpu来说,编码的格式是固定的。所以可以直接一个助记符来表示,这样科学家们写程序就方便多了,而这就是汇编程序的由来。因为汇编程序完成之后,可以再有一个专门的程序(就是要上文中所说的汇编器)来把编写的汇编程序编译成0和1.这样计算机也可以识别了,而汇编语言本身也方便了程序的编写和阅读。

编写汇编比直接编写二进制方便高效了太多。但是 随着计算任务的复杂,程序的规模越来越庞大,使用汇编程序也很累啊,那么是否有更简单的方式呢?所以科学家们发明了高级语言(比如 ,等),在编写程序的时候,使用C语言等编写,然后再使用 编译器将C语言程序翻译成汇编程序,汇编程序再使用汇编器编译成0和1,这样,cpu能识别的东西没有变化,但是对于编写程序的人,确实方便了很多。

通过以上的描述,我们就知道了高级语言的大概由来。也明白了我们所编写的各种高级语言,到了最后,其实都是转化为二进制执行。

而直接二进制格式的程序,我们称之为本地机器码(native code)。而类似那些 之类的 助记符,以及汇编的编写格式或标准,我们称之为 指令集

但是问题的关键来了。不同公司所生产的 cpu芯片。他们所使用的指令集不同啊, 这种芯片设计的事情,没有国际统一的标准,甚至像intel所代表的复杂指令集,和arm为代表的精简指令集,还有PowerPC、MIPS等指令集,它们指令集的设计思路就是不一样的。

所以为什么说C&C++语言不能实现跨平台运行,就是因为它编译出来 输出文件的格式,只适用于某种特定的CPU,而其他CPU不认识啊。


鼹鼠科技


程序的可移植性,本质上与实现它的编程语言无关。需要区分两个概念:编程语言和语言的实现。

编程语言

我们通常说的C/C++、Java、Python、JavaScript等编程语言,其实是指语法和语义上的一种规范。这种规范,定义了编写程序时必须要遵循的一种书写规则,只有符合规则才能被编译器正确识别和理解。

语言的实现

语言的实现,通常指的就是编译器了。对于同一种语言,不同的编译器可以有完全不同的实现方式。

以Python语言为例,就有很多种大相径庭的编译器实现。

  1. CPython - 解释执行。
  2. PyPy - JIT执行。
  3. Jython - 把Python源码编译成Java字节码,然后再Java虚拟机里执行。

C/C++也是一样,有很多种不同的实现,不过目前主流的实现,全都是编译成二进制的机器码执行的。但是,其实网络上也有很多C/C++的解释器,就是直接把C/C++的源码解释执行的。

我目前正在开发中的一个编译器项目,就支持C语言的绝大部分语法,目前的实现,也是解释执行的。

了解了编程语言和语言的实现后,再来说说程序的可移植性。

程序的可移植性

程序的可移植性,通俗来讲,就是你在一个环境上编写出来的代码,是不是能够很方便的把它放到另外一个环境上运行。这里的环境,包括软件环境和硬件环境。

  • 软件环境:通常指操作系统,以及程序所依赖的运行时环境,如系统支持库等。
  • 硬件环境:指程序运行的目标硬件,包括CPU、芯片组、外设等。

但从编程语言本身来说,理论上,任何高级编程语言都是可移植的,只不过实现可移植性的方式有所不同。

编写一次,到处编译

对于C/C++,目前几乎所有通用的实现都是直接把源码编成二进制目标代码。对于这种实现方式,要想进行移植,就必须要保证在不同的软件环境和硬件环境下,都要实现相应的编译器。比如要有Windows+x86的编译器、Linux+x86的编译器、Linux+ARM的编译器等等。也就是编写一次,到处编译。

所以,所谓的C/C++程序不能跨平台,是指被编译成二进制的可执行文件不能跨平台,而不是C/C++语言本身不能跨平台。

编译一次,到处运行

而Java则选择了不同的实现方式。Java典型的实现是,把Java源码编译成字节码,然后把字节码放到虚拟机中进行执行。

对这种方式,要实现可移植性,则必须要保证,在每种不同的软件环境和硬件环境中,都必须要实现相应的Java虚拟机。

比如要有Windows+x86的Java虚拟机,Linux+x86的Java虚拟机、Linux+ARM的Java虚拟机等。

这些不同的虚拟机,都遵循相同的Java字节码的规范。因此,只要把Java源代码编译成标准的Java字节码,就可以在所有的这些虚拟机中运行。


对编译器、OS内核、性能调优、虚拟化等技术感兴趣的朋友,欢迎关注!


分享到:


相關文章: