C为什么不能跨平台?

本文参考于https://www.cnblogs.com/jmsjh/p/7808764.html,由于只是简单的做一些笔记,难免出错,请包涵。

还有很多内容没有记录,详细内容请参考原文!

关于C不能跨平台性的分析

不管是使用的IDE直接点击个运行按钮还是使用gcc命令进行编译(gcc -o hello hello.c) ,得到最后的输出结果。但是实际上hello world程序的编译是这样的(以hello world程序为例)

 

 

预处理阶段:预处理器(cpp)把代码中#开头的行进行展开,比如头文件,宏扽分内容,修改最初的C文件。

编译阶段:编译器(ccl)将修改后的C文件,翻译成了另外一个文本文件,hello.s 这就是所说的汇编程序了,不同的CPU和平台环境,编译输出的汇编代码也是不同的。

汇编阶段:汇编器(as)将hello.s翻译成机器语言指令。把这些命令打包成一种叫做可重定位目标程序的格式。这其实就是二进制文件了。

链接阶段:编译过程最后还有一个链接阶段(程序调用过了printf函数),最后输出结果还是和上一步相同,是一个直接二进制文件。

结合图片和文字描述我们可以很好的了解hello world程序的编译过程。接下来我们简单认识一些汇编语言。

汇编就是比二进制操作起来更简单高效的一种语言,编写好的汇编程序需要通过汇编器编译成0和1这样计算机就可以识别。

汇编语言(英语:assembly language)是一种用于电子计算机、微处理器、微控制器,或其他可编程器件的低级语言。在不同的设备中,汇编语言对应着不同的机器语言指令集。一种汇编语言专用于某种计算机系统结构,而不像许多高级语言,可以在不同系统平台之间移植。--

有关二进制语言到汇编语言的发展故事,读者可自行搜索相关信息了解。

通过上面的描述,我们可以知道,我们所编写的各种高级语言,到了最后都是转化为二进制执行。

直接二进制格式的程序,我们称之为本地机器码,而一些助记符以及汇编的编写格式和标准,我们称之为指令集

输入的0和1中有一些是代表数据,一些代表的是指令,而指令是要被芯片固定识别的,芯片中要用晶体管(最初是电子管)组成的与或非门开识别这些指令和数据。因此不同的芯片对于同一段二进制所解析的含义是不同的。

那么问题的关键来了。不同的公司所生产的CPU芯片。它们所使用的指令集不同,这种芯片的事情,又不像TCP/IP协议那样,由国际统一的标准,甚至像intel所代表的复杂指令集,和ARM代表的精简指令集,他们指令集的设计思路就是不一样的。

C语言最后编译出来的二进制文件,不同的CPU识别含义不同,所以为什么说C语言不能实现跨平台运行,就是因为它编译出来的输出文件的格式,只适用于某种CPU,其他CPU不能识别。

  • 我们所说的跨平台运行,并不是指hello.c这个文本文件的运行。这个文本文件本身也没办法运行,运行的是它的编译结果hello,而这个有0和1组成的编译结果,不同的CPU和平台,解释的含义不同。所以C语言编译出来的结果,没办法跨平台运行。

  • 在不同的平台下,hello.c最后编译出来的文件的格式都不同。比如在linux编出来的是hello,在windows中编译出的是hello.exe,在mac中编译出来的是hello.out等等

  • 也有人会说,为了让linux下编写的一段hello程序运行在window上。我不拿随后的编译结果hello来直接运行,我在window环境下重新用IDE创建一个项目,同样的源代码copy一下重新运行,输出hello.exe,再在window上运行,行不行?答案是NO,因为不同的环境下,C语言的标准是有差异的。例如int类型,在有的平台上,可能16位表示,而有的平台上则是32位表示。所以不同环境下的同一个程序,会存在数据溢出之类的问题

    知道了C语言没办法跨平台运行,但是又没有一种方法可以让高级语言实现跨平台呢?

    思考编程中的一个场景,就是如果前端需要一个A格式的数据,但是后台只能够提供B格式的数据,那么我们怎么去做?很简单,写一个接口,把B格式的数据转化为A格式不就行了。这就是设计模式中的适配器设计模式

    关于跨平台也是一样的道理。CPU的指令集不同,不同平台编译出来的结果格式不同,那么我们可以在各个平台上运行虚拟机,然后制定某种编译结果的输出格式,我们的输出了某种格式的结果,直接在虚拟机上运行不就好了。。

    这其实就是java采取的方式

    class文件格式,虚拟机以及ByteCode(字节码)

    java程序编译出来的结果是hello.class,换句话说,他输出的结果是Class文件格式(也叫做字节码存储格式)。

    其实就是java虚拟机定义的二进制格式,称为字节码(ByteCode),是java虚拟机所能运行的格式。

    而各个平台的java虚拟机是不同的,但是我们编写的Java程序统一编译成特定格式的Class文件格式,然后Class文件可以在各个不同平台的java虚拟机上面运行,当然运行i俄国肯定是一致的,至于各个不同平台之间的差异,是存在于不同版本的java虚拟机,由Java虚拟机解析出相应平台所能识别的二进制文件。

    通过这种方式,java程序就实现了跨平台。

    平台无关性

    通过java虚拟机和Class文件格式,我们实现了平台无关性,这些适应于各个不同平台和CPU的工作的是由设计java虚拟机的人去做的,程序员就不需要去关心各个平台和cpu的差异啦!

    代码编译的结果,从本地机器码(NativeCode)向字节码(ByteCode)的转变,是存储格式的一小步,却是编程语言的一大步。

    虽然说名字是ByteCode,其实和NativeCode都差不多,反正都是定义了一套指令集,只是前者能被虚拟机执行引擎执行,后者能被物理机的CPU去执行罢了。

    因此,我们可以知道,Java虚拟机执行class文件和Java源文件并没有太大的关系。换句话说,Java的源文件编译的输出结果为class文件,而class文件能够被java虚拟机认识并执行,这是两个独立的过程,中间没啥关系。

    那么进而引申出另一个问题,某一种其他的编程语言,如果我设计出了一种对应的编译器,将其编译输出结果是class文件,那么该语言是不是也实现了跨平台?

    想到这一点,那么恭喜你,你发现了java虚拟机的另一种重要的特性,语言无关性。

    语言无关性

    Java虚拟机在执行class文件时,不知道也不关心这个class文件是怎么来的(这个class文件可以是任何一种语言的源文件编译来的,当然,就像直接编写汇编一样,你直接编写ByteCode也行,只要格式正确)。

    其实CPU在执行二进制的指令时,他不知道也完全不关心这些指令时怎么来的,是一个道理。

    很多人认为Java虚拟机执行Java是一件理所当然的事情,其实是一种错误的想法。

    Sun的开发设计团队在最初设计的时候就把Java的规范拆分成了Java语言规范《The Java Language Specification》及Java虚拟机规范《The Java Virtual Machine Specification》。

    并且在1997年发布的第一版Java虚拟机规范中就曾经承诺过:“In the future,we will consider bounded extensions to the Java virtual machine to provide better support for other languages”(在未来,我们会对Java虚拟机进行适当的扩展,以便更好地支持其他语言运行于JVM之上)。

实现语言无关性的基础仍然是虚拟机和字节码存储格式。Java虚拟机不和包括Java在内的任何语言绑定,他只与“class文件”这种特定的二进制文件格式所关联,class文件中包含了Java虚拟机指令集和符号表以及若干其他辅助信息。基于安全方面的考虑,Java虚拟机规范要求在class文件中使用许多强制性的语法和结构化约束,但任何一门功能性语言都可以表示为一个能被Java虚拟机所接受的有效的class文件。作为一个通用;及其无关的执行平台,任何其他语言的实现者都可以将Java虚拟机作为语言的产品交付媒介。

换句话说,Java虚拟机这个名字其实是一个误导,Java虚拟机和Java没啥关系,其实更应该叫做class文件虚拟机。

因为其他语言,只要有对应的编译器,输出结果就可以运行在Java虚拟机上,所以时至今日,涌现出Clojure、Groovy、JRuby、Jython、Scala一批运行在java虚拟机上的语言。

目前下图中的语言都已经可以运行在java虚拟机上。

还有很多其他的内容,在这里没有说明,请参考,戳这里

  • 12
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值