i3geek.com
闫庚哲的个人博客

简述编译型与解释型编程语言

概念:

  • 编译型语言:把做好的源程序全部编译成二进制代码的可运行程序。然后,可直接运行这个程序。
  • 解释型语言:把做好的源程序翻译一句,然后执行一句,直至结束!

区别:

  • 编译型语言,执行速度快、效率高;依赖编译器、跨平台性差些。如C、C++、Delphi、Pascal,Fortran。
  • 解释型语言,执行速度慢、效率低;依赖解释器、跨平台性好。如Java、Basic.

通俗的讲,编译语言是在编译后可以直接运行,而解释语言的执行需要一个解释环境。

 java很特殊,java程序也需要编译,但是没有直接编译称为机器语言,而是编译称为字节码,然后用解释方式执行字节码。

**********************************************************************************************

计算机不能直接理解高级语言,只能直接理解机器语言,所以必须要把高级语言翻译成机器语言,计算机才能值型高级语言编写的程序。

翻译的方式有两种,一个是编译,一个是解释。两种方式只是翻译的时间不同。编译型语言写的程序执行之前,需要一个专门的编译过程,把程序编译成为机器语言的文件,比如exe文件,以后要运行的话就不用重新翻译了,直接使用编译的结果就行了(exe文件),因为翻译只做了一次,运行时不需要翻译,所以编译型语言的程序执行效率高。

解释则不同,解释性语言的程序不需要编译,省了道工序,解释性语言在运行程序的时候才翻译,比如解释性basic语言,专门有一个解释器能够直接执行basic程序,每个语句都是执行的时候才翻译。这样解释性语言每执行一次就要翻译一次,效率比较低。

编译型与解释型,两者各有利弊。前者由于程序执行速度快,同等条件下对系统要求较低,因此像开发操作系统、大型应用程序、数据库系统等时都采用它,像C/C++、Pascal/Object Pascal(Delphi)等都是编译语言,而一些网页脚本、服务器脚本及辅助开发接口这样的对速度要求不高、对不同系统平台间的兼容性有一定要求的程序则通常使用解释性语言,如JavaScript、VBScript、Perl、Python、Ruby、MATLAB 等等。

java很特殊,java程序也需要编译,但是没有直接编译称为机器语言,而是编译称为字节码,然后用解释方式执行字节码。

但随着硬件的升级和设计思想的变革,编译型和解释型语言越来越笼统,主要体现在一些新兴的高级语言上,而解释型语言的自身特点也使得编译器厂商愿意花费更多成本来优化解释器,解释型语言性能超过编译型语言也是必然的。

在具体计算机上实现一种语言,首先要确定的是表示该语言语义解释的虚拟计算机,一个关键的问题是程序执行时的基本表示是实际计算机上的机器语言还是虚拟机的机器语言。这个问题决定了语言的实现。根据这个问题的回答,可以将程序设计语言划分为两大类:编译型语言和解释型语言。

编译型语言:
编译是指在应用源程序执行之前,就将程序源代码“翻译”成目标代码(机器语言),因此其目标程序可以脱离其语言环境独立执行,使用比较方便、效率 较高。但应用程序一旦需要修改,必须先修改源代码,再重新编译生成新的目标文件(* .OBJ)才能执行,只有目标文件而没有源代码,修改很不方便。现在大多数的编程语言都是编译型的。编译程序将源程序翻译成目标程序后保存在另一个文件中,该目标程序可脱离编译程序直接在计算机上多次运行。大多数软件产品都是以目标程序形式发行给用户的,不仅便于直接运行,同时又使他人难于盗用其中的技术C、C++、Fortran、Visual Foxpro、Pascal、Delphi、Ada都是编译实现的。

解释型语言:
解释型语言的实现中,翻译器并不产生目标机器代码,而是产生易于执行的中间代码,这种中间代码与机器代码是不同的,中间代码的解释是由软件支持的,不能直接使用硬件,软件解释器通常会导致执行效率较低。用解释型语言编写的程序是由另一个可以理解中间代码的解释程序执行的。与编译程序不同的是,解释程序的任务是逐一将源程序的语句解释成可执行的机器指令,不需要将源程序翻译成目标代码后再执行。释程序的优点是当语句出现语法错误时,可以立即引起程序员注意,而程序员在程序开发期间就能进行校正。对于解释型Basic语言,需要一个专门的解释器解释执行 Basic程序,每条语言只有在执行才被翻译。这种解释型语言每执行一次就翻译一次,因而效率低下。一般地,动态语言都是解释型的,如Tcl、Perl、Ruby、VBScript、 JavaScript等。

混合型:
Java很特殊,Java程序也需要编译,但是没有直接编译称为机器语言,而是编译称为字节码,然后在Java虚拟机上用解释方式执行字节码。Python 的也采用了类似Java的编译模式,先将Python程序编译成Python字节码,然后由一个专门的Python字节码解释器负责解释执行字节码。(Java虚拟机对字节码的执行相当于模拟一个cpu,而ruby1.8–在虚拟机还未出现前–是通过解释成语法树执行。)

**********************************************************************************************

解释型的编程语言

我们知道,任何编程语言编写的程序归根到底都是由底层机器的机器代码(01序列)执行的,无论是编译型语言还是解释型语言。而任何高级编程语言程序的源代码都是一个字符序列,这个字符序列到底层的01序列是通过编译器或解析器经过多次转换完成的。

图1 编程语言的层次结构

这个层次结构中,从高到低越来越接近于机器硬件。机器代码就是01序列,汇编语言就是描述本地机器的指令集体系结构,而高级语言就包含相应的数据结构和语法结构,更接近人类的语言习惯。因此,层次越高就越面向于人类。在计算机科学中,CPU被抽象为指令集体系结构,这个指令集描述了CPU所有完成的所有功能。所有的程序都经过编译或解释转化为这个指令集表示的机器程序。在指令集中指令可以按功能划分为:

1. 数据传输指令,用于读写内存、寄存器。

2. 算术与逻辑运算指令,比如:addl执行双字(32bit)的加法,andl双字的按位与。

3. 控制流指令,用于实现高级编程语言中的分支、循环等控制结构。

4. 过程调用指令,用于实现函数调用,分配、恢复栈帧等操作。

通过观察C程序的机器代码可以发现由C程序转化为机器代码,主要有数据类型和控制结构的转换。下面以x86指令集说明:

1. 数据类型的转换:在底层,x86指令对于数据是不区分逻辑类型的,也就是不分int,float,double。所有的数据按照其所占的字节数被归类为字(16个字节,Word)、双字(32个字节,Double Words)、四字(64个字节,Quad Words)。一个指令操作的数据类型是由这个指令的后缀表示的,比如mov指令,movw操作字,movl操作双字。也就是说高级语言的程序中的不同数据类型反映到底层指令集上主要体现是指令的不同。比如,将上述C程序中的result类型改为short,在相应的汇编代码中的mov指令会由movl转换为movw。当然,还有一个问题就是C语言中的具体数据类型,在机器代码中是如何存储表示的。这应该是gcc编译器的职责,比如对于int,首先gcc需要知道底层指令集如何编码int,采用什么编码方式,字节顺序是Big-endian还是Little-endian等。在知道底层的实现方式后gcc才能将表示整型数字的字符串编码为相应的二进制形式。而对于数组、struct和union这些数据结构会转化为相应的内存地址加偏移量的形式。

2. 控制结构的转换:控制结构就是执行指令的流程。在x86中,所有的指令集都是顺序执行。要实现分支、循环等结构,必须具备go形式的跳转指令,以及相应的条件判断指令。CPU中有一组条件码寄存器,指示算术或逻辑运算的状态(计算结果是否溢出、为0或者是负数等)。执行条件运算指令可以测试一个条件,比如”cmpl $1, %edx”比较直接数1与寄存器%edx中存放的数的大小,并将结果存入条件码寄存器中。接下来执行条件跳转指令,根据条件码寄存器中的状态进行判断是否进行跳转。比如“jg .L10”是在前一条的cmpl指令结果返回大于的情况跳转到L10,否则执行下一条指令。

当然,在进行函数调用时,还要在底层用机器码对其进行描述。我们知道,计算机科学中用栈来实现函数的调用(叫做调用栈),栈中存放栈帧。每一次函数调用对应一个栈帧,栈帧中包含该方法的局部变量、保存的寄存器值等数据。这样函数的调用和返回就对应着栈帧的入栈和出栈。CPU的寄存器组中,有两个专门用于实现方法调用,分别是%esp和%ebp。%esp是栈指针寄存器,存放当前函数栈栈顶的内存地址。%ebp是帧指针寄存器,在%esp和%ebp之间的内存地址序列就对应于当前函数的栈帧。由于函数调用、返回与栈帧的关系很密切,所以可以将以此函数调用过程描述为:

1. 初始化被调用函数的栈帧,并将其入栈。也就是调用函数过程,通过call指令实现。

2. 执行被调用函数。

3. 恢复调用函数的栈帧,将被调用函数的栈帧出栈。也就是函数返回的过程,通过ret指令实现。

对于初始化、恢复栈帧实际上都是%esp和%ebp的调整,还要包括传参和返回值的问题,这些都是由编译器实现的。

上面介绍了C语言和机器语言的关系,下面看一下其他类型语言的实现机制。首先,我们可以把编程语言分为编译型语言、解释型语言和虚拟机语言。编译型语言直接被编译成本地机器代码,比如C、C++。解释型语言是通过解释器执行,比如javascript、shell、python等。虚拟机语言运行在虚拟机上,需要被编译成虚拟机代码,由虚拟机执行,比如java。虽然python也有自己的虚拟机,但是不需要编译,所以把它归类为解释型语言。

图2 编程语言实现结构

通过上文的分析、我们知道对于一门语言最重要的是数据类型、控制结构和语法结构以及系统调用。从上图可以看出,C和C++更接近于底层硬件,但是不能像汇编语言一样可以直接访问寄存器等硬件。而python和java相对于C和C++的抽象层次又高了一层,它们不能通过指针直接访问内存。从机器语言->汇编语言->系统语言(C和C++)->解释型语言(python)和虚拟机语言(java),抽象层次越来越高,越贴近于人的思维,不需要考虑那么多细节;同时,程序员的自由度和程序的运行速度越来越低。下面从低向高j讨论一下。

在底层,汇编语言会经过汇编器转换为机器代码。比如,通过gcc编译C程序时,会调用汇编器进行汇编。通过汇编器和汇编语言这一层次,可以很好的隔离底层机器硬件的实现细节。不同的处理器具有与之对应的汇编器,将汇编语言汇编成该处理器支持的指令集。这样就是实现了汇编语言这一层的移植性。

在C和C++系统编程语言这一层,会通过编译器完成语言元素到汇编语言的映射。比如前文描述的,数据类型、控制结构、函数调用等结构的转换。

python是解释型语言,它通过python解释器实现向底层语言的映射。我们知道python虚拟机是由C语言编写的,所以python程序会转化为C程序而执行。比如,python中的所有对象都会在C中有对应的PyObject结构体。python的list、dict等数据类型也要在C中有对应的表示。而像生成器、迭代器等语法结构需要相应的支持。

而虚拟机是模拟一个指令集的程序,所以它自身有一套独立于具体硬件、操作系统的指令集。需要通过底层语言实现这套指令集。虚拟机本身也有自己的数据类型系统、语言结构等。比如,java虚拟机上支持的数据类型有基本数据类型和引用类型,也支持tableswitch和lookupswitch等实现switch语法结构的字节码指令。对于这些语言元素映射到底层语言的实现方式可以不同的方式。首先是解释器模式转化为C++,还有就是JIT直接编译成本地机器代码。

像java这样的虚拟机语言会被编译器编译成虚拟机本地的机器代码,然后再虚拟机上执行,这里就需要向javac编译器实现java语言的数据类型、语言结构和java虚拟机上的数据类型、语法结构的映射。

通过谈论,可以看出编译器和解释器以及虚拟机在编程语言中的重要性,它们都是编程语言可以在计算机上运行的基石。一门编程语言的编译器、解释器或者虚拟机可以很大程度上影响这门语言的执行效率。因为它们在进行语言转换时会进行很多的优化以提高执行效率。这也是为什么JVM上有那么多优秀的语言,因为JVM很强大。所以,要深入语言的底层,要学会编译器、解释器和虚拟机的实现,这方面还需要下功夫啊。

**********************************************************************************************

1.动态语言Dynamically Typed Language

例如:ECMAScript(JavaScript)、Ruby、Python、VBScript、php

也叫动态类型定义语言

与静态类型定义相反,一种在执行期间才去发现数据类型的语言,

动态语言是指程序在运行时可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化。

动态语言的类型检查是在运行时做的。

它的优点是方便阅读,不需要写非常多的类型相关的代码;

缺点是不方便调试,命名不规范时会造成读不懂,不利于理解等。

目前java平台下的动态语言有Groovy、nice、BeanShell、Jython、JRuby、Rhino(JavaScript)、 Jacl(TCL)、Bistro(SmallTalk)、Kawa(Lisp/Schema),真是越来越多了。java下这么多的动态语言建议选择 Groovy,感觉血统较为正宗,兼容Java的语法,java程序员学习起来较为容易,上手较快。

2.静态语言Statically Typed Language

例如:C、C++、Java

也叫静态类型定义语言。即一种在编译时,数据类型是固定的语言。大多数静态类型定义语言强制这一点,它要求你在使用所有变量之前要声明它们的数据类型。

在使用数据之前,我们必须首先定义数据类型,这些数据类型包括int ,float,double等等。就相当于在使用它们之前,首先要为它们分配好内存空间。

静态类型语言的主要优点在于其结构非常规范,便于调试,方便类型安全;

缺点是为此需要写更多的类型相关代码,导致不便于阅读、不清晰明了。

3.强类型定义语言

一种总是强制类型定义的语言。Java和Python是强制类型定义的。如果你有一个整数,如果不显示地进行转换,你不能将其视为一个字符串

4.弱类型定义语言

一种类型可以被忽略的语言,与强类型定义相反。VBScript是弱类型定义

的。在VBScript中,可以将字符串 ’12′ 和整数 3 进行连接得到字符串 ’123′,

然后可以把它看成整数 123,而不需要显示转换。

5.脚本语言

脚本语言代表一套与系统程序设计语言不同的协定。

它们牺牲执行速度和与系统程序设计语言相关的类型长度而提供更高的编程创作力和软件重用。

脚本语言更适合在联系复杂的应用程序中进行胶着。

为了简化连接组件的工作,脚本语言被设计为无类型的,脚本语言一般是面向字符的,因为字符为许多不同的事物提供了一致的描述。

事实上,脚本语言都是动态语言,而动态语言都是解释型语言,不管它们是不是面向对象。

赞(0)
未经允许不得转载:爱上极客 » 简述编译型与解释型编程语言
分享到: 更多 (0)

评论 2

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    分享很给力 😛

    威客5年前 (2014-12-30)回复
    • 谢谢!

      yan5年前 (2015-01-01)回复