比较C、Java、Python三种语言的编译原理

文章目录

  • C语言
  • Java语言
  • Python语言
  • 解释型语言和编译型语言


C语言

C语言作为入门语言,再编程界的地位可谓元老级。UNIX 操作系统是用纯C语言编写的;Windows 操作系统的内核也是用C语言编写的;Linux 操作系统仍是用纯C语言编写的。
优点:
简洁紧凑、灵活方便
运算符丰富
数据结构丰富
结构式语言
限制少,自由程度大
允许直接访问物理地址,直接对硬件进行操作
代码质量高,程序执行效率高
适用范围大,可移植性好

缺点:
危险性高: 例如将一个浮点型数据赋给一个整型变量,编译时只会警告,因为会丢失精度,但是在 Java 中这样写就会报错。往往会产生漏洞,易受到黑客攻击。
开发周期长: C语言是面向过程的语言,面向过程语言的一个特点就是写大项目的时候很容易“崩溃”。编程时往往采用面向对象的编程语言
安全性欠缺
复用性差
维护性差
扩展性差
可读性差


C语言运行过程包括预处理、编译,汇编,连接
预处理得到**.i文件,编译得到.s文件,汇编得到.o**文件,链接生成一个可执行目标文件

预处理: 是C语言程序从源代码变成可执行程序的第一步,主要是C语言编译器对各种预处理命令进行处理,包括头文件的包含、宏定义的扩展、条件编译的选择等。打印出预处理之后的结果:gcc -E hello.c 或者 cpp hello.c这样我们就可以看到源代码中的各种预处理命令是如何被解释的,从而方便理解和查错。
使用预处理器(cpp)

编译: 编译之前,C语言编译器会进行词法分析、语法分析(-fsyntax-only),接着会把源代码翻译成中间语言,即汇编语言。
编译程序工作时,先分析,后综合,从而得到目标程序。所谓分析,是指词法分析和语法分析;所谓综合是指代码优化,存储分配和代码生成。为了完成这些分析综合任务,编译程序采用对源程序进行多次扫描的办法,每次扫描集中完成一项或几项任务,也有一项任务分散到几次扫描去完成的。
大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,但也有的编译程序则先产生汇编语言一级的符号代码文件,然后再调用汇编程序进行翻译加工处理,最后产生可执行的机器语言目标文件。 使用编译器(ccl)

汇编: 把作为中间结果的汇编代码翻译成了机器代码,即目标代码,不过它还不可以运行。使用汇编器(as)

连接: 链接是处理可重定位文件,把它们的各种符号引用和符号定义转换为可执行文件中的合适信息(一般是虚拟内存地址)的过程。链接又分为静态链接和动态链接,前者是程序开发阶段程序员用ld(gcc实际上在后台调用了ld)静态链接器手动链接的过程,而动态链接则是程序运行期间系统调用动态链接器(ld-linux.so)自动链接的过程。
静态链接过程主要是把可重定位文件依次读入,分析各个文件的文件头,进而依次读入各个文件的节区,并计算各个节区的虚拟内存位置,对一些需要重定位的符号进行处理,设定它们的虚拟内存地址等,并最终产生一个可执行文件或者是动态链接库。这个链接过程是通过ld来完成的,ld在链接时使用了一个链接脚本(linker scripq),该链接脚本处理链接的具体细节。这里主要介绍可重定位文件中的节区(节区表描述的)和可执行文件中段(程序头描述的)的对应关系以及gcc编译时采用的一些默认链接选项。使用连接器(ld)

其实操作系统使用三种链接方式:1.静态链接、2.装入时动态链接、3.运行时动态链接

静态链接:在程序运行之前,先将个目标模块及它们所需的库函数连接成一个完整的可执行文件(装入模块),之后不在拆开。
装入时动态链接:讲各目标模块装入内存时,边装入边链接的链接方式。
运行时动态链接:在程序执行中需要该目标模块时,才对它进行链接。其优点是便于修改和更新,便于实现对目标模块的共享。

以上四个步骤在linux系统常见。


Java语言

Java整个编译以及运行的过程相当繁琐。
Java程序从源文件创建到程序运行要经过两大步骤
1、源文件由编译器编译成字节码(ByteCode)
2、字节码由java虚拟机解释运行。
因为java程序既要编译同时也要经过JVM的解释运行,所以说Java被称为半解释语言( “semi-interpreted” language)。

编译: 创建完源文件之后,程序会先被编译为**.class文件。
Java编译一个类时,如果这个类所依赖的类还没有被编译,编译器就会先编译这个被依赖的类,然后引用,否则直接引用,这个有点象
linux中的make**。
如果java编译器在指定目录下找不到该类所其依赖的类的**.class文件或者.java源文件的话,编译器话报“cant find symbol”**的错误。
编译后的字节码文件格式主要分为两部分:常量池和方法字节码
常量池记录的是代码出现过的所有token(类名,成员变量名等等)以及符号引用(方法引用,成员变量引用等等)。
方法字节码放的是类中各个方法的字节码。

运行: 两个步骤:1、类的加载 2、类的执行
JVM在使用到程序中的哪一个类,才会把这个类加载到内存中,且只加载一次。

序运行的详细步骤:
**1.**在编译好java程序得到MainApp.class文件后,在命令行上敲java AppMain。系统就会启动一个jvm进程,jvm进程从classpath路径中找到一个名为AppMain.class的二进制文件,将MainApp的类信息加载到运行时数据区的方法区内,这个过程叫做MainApp类的加载。
**2.**然后JVM找到AppMain的主函数入口,开始执行main函数。
3. main函数的第一条命令是Animal animal = new Animal(“Puppy”);就是让JVM创建一个Animal对象,但是这时候方法区中没有Animal类的信息,所以JVM马上加载Animal类,把Animal类的类型信息放到方法区中。
**4.**加载完Animal类之后,Java虚拟机做的第一件事情就是在堆区中为一个新的Animal实例分配内存, 然后调用构造函数初始化Animal实例,这个Animal实例持有着指向方法区的Animal类的类型信息(其中包含有方法表,java动态绑定的底层实现)的引用。
**5.**当使用animal.printName()的时候,JVM根据animal引用找到Animal对象,然后根据Animal对象持有的引用定位到方法区中Animal类的类型信息的方法表,获得printName()函数的字节码的地址。
**6.**开始运行printName()函数。
比较C、Java、Python三种语言的编译原理_第1张图片


Python语言

刚开始学习python的时候,了解到python是一门解释型语言。
**解释型语言:**在程序运行的时候,通过解释器对程序逐行作出解释,然后直接运行

运行Python程序的时候,Python解释器会执行两个步骤:
1. 把源代码编译成字节码。把程序的字节码保存为一个以.pyc为扩展名的文件
**2.**把编译好的字节码转发到Python虚拟机(PVM)中进行执行。

有两个概念需要了解一下:
PyCodeObject和pyc文件
PyCodeObjectPython编译器真正编译成的结果。
①当python程序运行时,编译的结果则是保存在位于内存中的PyCodeObject中,当Python程序运行结束时,Python解释器则将PyCodeObject写回到pyc文件中。
Python程序第二次运行时,首先程序会在硬盘中寻找pyc文件,如果找到,则直接载入,否则就重复①的过程
pyc文件其实是PyCodeObject的一种持久化保存方式。
只要源代码没有改变,生成的.pyc文件可以重复利用,提高执行效率


解释型语言和编译型语言

计算机是不能够识别高级语言的,所以当我们运行一个高级语言程序的时候,就需要一个“翻译机”来从事把高级语言转变成计算机能读懂的机器语言的过程。这个过程分成两类,第一种是编译第二种是解释

编译型语言在程序执行之前,先会通过编译器对程序执行一个编译的过程,把程序转变成机器语言。运行时就不需要翻译,而直接执行就可以了。

解释型语言就没有这个编译的过程,而是在程序运行的时候,通过解释器对程序逐行作出解释,然后直接运行。

解释型语言和编译型语言的优缺点,因为编译型语言在程序运行之前就已经对程序做出了“翻译”,所以在运行时就少掉了“翻译”的过程,所以效率比较高。但是我们也不能一概而论,一些解释型语言也可以通过解释器的优化来在对程序做出翻译时对整个程序做出优化,从而在效率上超过编译型语言。

对于某种语言不能简单地归为解释型语言或者是编译型语言,比如Java语言就是半解释语言。
Java首先是通过编译器编译成字节码文件,然后在运行时通过解释器给解释成机器文件
,就是先编译后解释。


参考的一下三个网站:

参考网址:
C语言:https://www.cnblogs.com/pipicfan/archive/2012/07/10/2583910.html
Java:https://www.cnblogs.com/qiumingcheng/p/5398610.html
Python:https://www.cnblogs.com/kym/archive/2012/05/14/2498728.html

你可能感兴趣的:(编译,python,java,c语言,python,编程语言)