linux和windows系统开发语言,编译原理(C/C++的源码到机器语),汇编,C/C++与Java的区别

linux和windows系统开发语言

windows:汇编制、C、VB、C++;
linux:汇编、C;linux gui主要有两大类:gtk是C和C++的,qt是C++的。

编译原理

集成开发环境(IDE,Integrated Development Environment )、

软件开发的环境需要什么?一个IDE,一个OS,一个硬件设备,没错,这个实质是软件进展的三个层集。

在很久很久以前(几十年),软件就是直接开发在硬件设备上的,用纸带有无孔标识二进制位,此时的开发语言是机器码,软件直接对接硬件设备;后来很不方便,尤其不方便复用,然后,有了汇编,有了简单的编译环境,然后逐渐发展成为OS内核;

然后有了高级语言:C等,为了更加高效友好的开发,有了最初期的IDE。软件的规模越来越大,行业分工越来越细,开发的效率也越发的重要,以Android为例,从Eclipse到google官方所推的Android Studio,IDE的功能越来越强大,这个解放了程序员,但也控制了程序员,有谁还会知道,写在这些IDE中的代码行

为什么可以执行在手机上?中间有什么过程,是什么模块在控制这些过程?是完全由IDE实现的吗?我相信能答出这个问题的人是少数。古人说过,要知其然,还要知其所以然,只会使用IDE去写固定的功能模块,只是一个普通码农,要想用好一个工具(包括IDE及语言),还要了解其实质,要明白,我们写好的代码,是怎么运行到硬件环境上的。

这个话题很大,包括编译原理、OS结构、硬件驱动、各种语言等。

看一下C/C++及Java由代码到机器码的过程,有了机器码之后,再后面的运行细节,离软件开发离得较远,和开发优质软件平台(除OS外)关系也不太大。

由源码到机器码,C/C++与Java的实现并不相同,为什么要放在一起呢?

  1. 从语言代来讲,C为二代面向过程语言、C++为三代面向对象语言,Java为参考C++所设计的三代面向对象语言,其本身是有传承的,语言会传承,编译运行环境同样是传承的,对比着看,可以看出优化方向;
  2. 个人认为,C/C++、Java作为静态语言,其应用范围、语言特性,编译运行原理非常有代表性,尤其是Java在做跨平台,之后JVM上可以运行其他的动态语言,可以说Java的编译运行可以代表一部分希望跨平台的动态语言,比如Kotlin。

C/C++的源码到机器语言过程

补充:

在Windows系统上,C/C++语言不需要像JAVA语言一样配置运行环境,因为Windows系统是由C++编写,自带C/C++虚拟机

我们只需要安装带编译器的IDE或者编辑器就可以编写C/C++程序

推荐一些IDE:

  1. C-Free
  2. Dev-C++

分为四个大步骤:预编译、编译、汇编、链接,在C/C++中统称为编译。

    (一)预编译所处理的过程包括:

        a.展开宏定义#define

        b.处理条件预编译指令#if等

        c.处理#include

        d.删除注释

        e.为Debug及日志填加行号

        f.保留#pragma

    (二)编译所处理的过程包括:

        a.词法分析:使用扫描器将源码分隔为一系列的记号(Token),即源码中的不可分隔项,较为容易理解,比如下面的:

        int index = (2 + 8) * c  会被折分为 :int index = ( 2 + 8 ) * c  10个token,左右半括号各为一个

        b.语法分析

        将a中分出来的Token,映射为语法树

        c.语义分析

        在b中语法树的基础上,分析是否有错误语义,编译器所能分析的语义为静态语义,包括:声明是否正确、类型是否匹配、类型的转换是否符合要求

        d.经过前面三个过程后,将源代码转换为中间语言,可以理解为将c中通过的语法树通过一定的规则拍平,变为类假于目标代码的结构,为什么要引入中间语言呢,目标语言很多时候是硬件相关的,而中间语言与硬件无关。

        e.生成目标代码并进行相应优化

        目标代码的文件组织格式为.o文件,其内部的格式与具体设备有很大关系,比如在Android中,对arm7和x86CPU要编译出不同的so文件,此处同理,不同的硬件环境生成不同的目标代码。目标代码的优化也是针对不同情况有不同处理,在此不再展开,感兴趣的同学可以参考编译原理相关书籍。

    (三)汇编:汇编的目的是把汇编语言转为机器语言,基本是一条转一条,没啥特殊的

    (四)链接:链接是要解决目标文件之间的互相依赖关系,当a文件中的aa方法中调用了b文件的bb方法时,在汇编完成后,a文件的bb方法并没有准确的内存地址,链接后会转换为虚拟地址,虚拟地址可以依据一定的规则转换为实际地址,即可以运行时找到该方法。链接过程包括:地址和空间分配、符号决议和重定位

    Java语言从源代码到机器码的过程要比C/C++复杂,Java追求的是一次编译,多次运行的跨平台特性,因而在分层上更为彻底。可以分为编译期和运行期两个周期:

  1. 编译期的输入为源代码.java文件、输出为字节码.class文件
  2. 运行期输入为字节码.class文件,输出为机器码

为何这么做可以跨平台呢,JVM定义了严格的.class文件的格式,Java文件需要严格按照定义编译为.class文件,然后可以拿到各个平台各个版本的虚拟机上运行。如果其他的语言编译完毕后也遵循.class文件格式,也可以在JVM上和Java一样运行。

先来看Java的编译期,有以下几个过程:

    (一)生成语法树及符号表的过程:

        a.首先进行词法分析,将源码的字符流分解为token的集合,token的含义可以参考上面所提到的,即不可折分的个体

        b.语法分析,根据token集合构建抽象语法树

        c.生成符号表:符号表是一个类key-value结构的集合,记录符号的类型、结构、定义,支持增加与删除

    (二)处理Java语言中的注解,修正(一)中生成的语法树

    (三)语义分析,与C/C++的语义分析类似,进行一些语义正确性检测,具体包括:

        a.标注检查:类型声明及赋值是否合适

        b.常量折叠:将 1 + 2 直接用3替代

        c.上下文的语法检测,如变量使用前是否赋值等

        d.解语法糖,为了提升开发效率,Java提供了许多的语法糖,比如:泛型、变长参数、自动装箱等,在此过程中,会将这些语法糖转为JVM所规定的格式。具体可以查看Java虚拟机中相关语法编译期的处理

    (四)生成字节码,生成字节码阶段主要是两个事情:

        a.按照抽象语法树及符号表生成相应的字节码

        b.针对Java语言的特点,进行些必要的填充,比如:字符串的+转为StringBuilder等

    经过上述四个过程,Java中的编译期即进行完毕了,可以获取到.class文

    继续分析下Java的运行期,先看下运行期有什么特点。首先,运行期离Java语言的使用者,离得较远,运行期处理的是字节码到机器码之间的转换,Java语言的使用者不需要了解这些细节,也可以进行高效的开发。理解这些细节,以我当前的体验,对于开发者来讲,主要是增长知识面,对开发的影响也不大。那运行期和什么样的开发者关系较大呢,没错,就是虚拟机开发人员或者需要优化虚拟机的人员,比如,服务器的运维人员。

    Java运行期的工作并非是固定的,最基本的,也是最简单的,当然,也可以理解为保底的,是使用解释器将字节码转换为机器码,该原理相当的清晰明确,有以下步骤:

    a.按一定的规则寻找相关的类,首先,根据环境变量找到java类库的位置,然后根据双亲委托模式找到准确的类位置

    b.然后就很简单了,JVM字节码可以直接逐条解释,只是JVM是基本于栈的指令集,条数较多,解释较为耗时。

    另一种Java运行期的处理为JIT,是一种Java动态编译方法,也是当前Java虚拟机效率逐渐追上C/C++的主要因素,分为两种:Client Compiler和Server Compiler        

   Server Compiler是一个经过充分优化的高级编译器,包括但不限于:无用代码擦除、基本块重排序等,其技术极为复杂,感兴趣的同学可以查阅专门的资料研究。选用上述那种Compiler进行处理,要视环境的需要而定。

    JIT并非完全取代解释器,只是在需要优化的高频方法或高频模块(比如多次for循环)才会介入,其他时候仍采用的是解释器的处理方式。

     针对Java的源码到机器码的处理,上面提到了两种编译运行方式分别为:编译为字节码+解释器、编译为字节码+JIT+解析器。目前还是另一种方式,为AOT,一种静态编译方式,在编译期,即将源码编译为机器码,其好处也是显然的,运行器无JVM处理时延,效率最高,但缺点也很明显:(1)牺牲了Java的跨平台特性,机器码一定是平台相关的(2)编译算法复杂度相当高,JIT在运行期,通过运行时数据收集,制定是否编译机器码,但AOT在编译期就要收集到这些。

   

总结一下,总共提到的Java的源码到机器码的处理为三种:

        a.编译为字节码+解释器

        b.编译为字节码+JIT+解析器

        c.AOT直接编译为机器码

    Java目前应用范围要大于C/C++,场景也更为丰富,采用那种方式,要看那些场景更为合适,比如,Android采用的变异后的b方式(编译后的输出为dex文件,运行前要将dex文件转化为oat格式,运行时要处理的格式为oat格式,运行时的指令集为基于寄存器的)

    从上面对C/C++及Java编译运行的分析,可以看出,编译的基本原理及流程是相近的,然后加入了后期的优化,尤其是Java,为改善编译及运行效率(JIT、AOT、字节码等),做了大量的工作,至Android虚拟机,又根据平台特点,在Java的基础上做了大量的改进。C/C++将编译运行封装起来,对外黑盒,代码的开发人员是难以干扰其过程的。

而JVM,只是将抽象树之前的处理封装起来,后续的处理可以人为干预(比如JIT就是对解释器的一种优化)。因而JVM从复杂度及涉及的技术面、调优人员来讲规模都要远大于C/C++编译运行环境。
 

 

在C,C++,java和python运行时解释器和编译器的区别

集成开发环境(IDE,Integrated Development Environment )

在这四种语言里有两种不同的程序运行过程:

1.  高级语言-> 机器代码:

C和C++的编译过程有几个步骤:

> 预编译: 将.c 文件转化成 .i文件),使用的gcc命令是:gcc –E,对应于预处理命令cpp

> 编译: 将.c/.h文件转换成.s文件, 使用的gcc命令是:gcc –S, 对应于编译命令 cc –S

> 汇编:将.s 文件转化成 .o文件,使用的gcc 命令是:gcc –c,对应于汇编命令是 as

> 链接:将.o文件转化成可执行程序,使用的gcc 命令是: gcc,对应于链接命令是 ld

前三步都可以叫做编译,它的输出是一条条机器指令,在链接中会把机器指令和目标文件库文件结合起来,生成系统可执行的文件.exe。

2. 高级语言-> 字节码 ->机器代码:

2.1 java

java 在执行过程中先利用javac将源文件编译成.class字节码,然后在jvm上继续解释和编译成可执行的机器代码。你可能注意到在jvm过程中同时有编译和解释的过程,这是跟jvm运行机制有关:

 

在理解这幅图之前,先了解下JIT的历史。一开始Sun公司采用classic vm作为JVM,但是饱受“Java比C++慢的多”的诟病。后来Sun公司引入HotSpot作为虚拟机,并引入JIT(Just In Time)技术。JIT又称即时编译器,虽说是编译器,它跟javac编译器的功能不同。JVM有三种运行模式:解释模式,编译模式,混合模式。上图对应的是混合模式,其流程为:

1.源代码经过编译器成为.class文件,也就是字节码。

2.程序字节码经过JIT判断,是否属于热点代码,例如循环或者频繁调用的方法。

3.如果是,被JIT编译成机器字节码,对应具体硬件处理器(如sparc,intel)。

4.如果不是,被JIT解释器解释执行。

5.操作系统和类库的调用。

6.硬件。

所以JIT是一个解释器和编译器的集合,某些“热点代码”可以通过编译来节省逐条解释的耗时,其他代码仍旧通过解释器执行。这样的混合模式执行要比纯编译模式快。那么为什么纯编译模式要比混合模式慢呢?博客中给出的回答是:

编译执行不加筛选的将全部代码进行编译机器码不论其执行频率是否有编译价值,在程序响应时间的限制下,编译器没法采用编译耗时较高的优化技术(因为 JIT的编译是首次运行或启动的时候进行的!),所以,在纯编译执行模式下的java程序执行效率跟C/C++也是具有较大差距的。

看来java也不是完全的解释性语言。

2.2 python

python的编译过程是自动运行的,并不需要人工另外的操作。

py文件被编译成.pyc 字节码文件。这个字节码文件跟平台无关。接下来由pvm解释执行这个字节码文件,每一次负责将一条字节码文件语句翻译成cpu可以直接执行的机器代码,然后在接下来下一句。

对于python来说,没有针对机器代码的编译,每一条语句的执行都是直接对源代码或者中间代码进行解释运行。而少了这个编译的过程,使得解释型语言运行较慢。另外,在逐条解释的过程中,效率也较低。

解释型语言也有优点,比如它的平台无关性,另外,具体逐条解释的时候会进行动态优化,有时不见得比编译型的慢。

python最开始也有一个编译的过程,所以跟java一样,也不是纯的解释性语言。

总结下来,所谓的解释性语言主要有三种:

1. 直接运行高级编程语言:比如shell内置的解释器。

2. 将源代码转化成一些有效率的字节码或者中间代码,然后再解释运行:比如pvm

3. 将源代码编译成字节码或者中间代码,并指示处理器运行编译后的程序:比如JIT 

java的底层虚拟机就是纯C写的,如果用JAVA写个操作系统,执行的过程就是java-C-汇编-机器指令这样了,就是效率问题,别说操作系统了,就是windows上跑的程序,都几乎没java写的,99%是C++写的,100%的图形处理程序是C++写的

java的特点是oop,执行效率相比较c/c++较低,java开发效率高,适合编写应用和网页

c/c++的特点是指针,能够直接跟内存打交道,适合性能要求较高的场合。

某些场合c的性能还不能满足要求的话,可以用更低级的汇编语言。

C/C++:        

C/C++代码——编译(不同的系统编译出不同的机器码,所以同一个C/C++文件不一定可以在某些系统执行,因为编译出的机器码不同)——机器码————在操作系统中由硬盘读取到内存中运行——内存——CPU——输出结果

Java:     

Java代码————编译得到字节码文件(.class)————JVM执行字节码文件(字节码在虚拟机上运行,虚拟机相当于翻译官,不同的系统JVM不同,转换规则不同,把同一个字节码文件转换为相应的系统的机器码)————机器码在相应系统运行——内存——CPU——结果

由于JVM的存在,只需在不同的系统上安装相应的JVM,同一个.class文件在相应的系统的JVM运行就会输出相应系统能解析的机器码,从而成功运行。

这就是,一次编译,到处运行。

打个比方,就是:

一本汉字写的书(源码),去到不同的国家(系统),每个国家有相应的翻译官(JVM虚拟机)把汉字翻译成其所在国家的文字(比如这本书传到英国被翻译成英文书),之后就可以在这个国家流传开了(相当于程序成功运行)。

JAVA有两特性:

移植性:一次编译,到处运行(上面已解释)

安全性:自动回收内存中不常用的命令垃圾,防止内存溢出。

发布了651 篇原创文章 · 获赞 663 · 访问量 59万+

猜你喜欢

转载自blog.csdn.net/qq_38998213/article/details/105469004