2.1 编程语言的概念
程序设计语言是人们为了描述解题步骤(即编程序)而设计的一种具有语法语义描述的记号。计算机语言的种类非常的多,总的来说可以分成机器语言,汇编语言,高级语言三大类。
在计算机角度,每一种CPU类型都有自己可以识别的一套指令集,计算机不管这个程序是用什么语言来编写的,其最终只认CPU能够识别的二进制指令集。
2.1.1 机器语言
在早期计算机刚发展的时代,人们都是直接输入01010101这样的没有语义的二进制指令(俗称机器语言)来让计算机工作的,这对编程人员的要求极高,一方面要求编程人员对计算机的硬件结构非常熟悉,另一方面这些机器指令的可读性几乎没有,没人愿意直接编写那些没有可读性、繁琐、费时,易出差错的二进制01代码,所以后来才出现了编程语言(包括汇编与高级语言)。
机器语言执行过程如图所示。
机器语言的特点是:代码编写复杂、易错、没有语义,对程序员非常不友好,但由于是机器指令,可以直接被计算机所识别,效率高。
2.1.2 汇编语言
为了克服面向CPU的指令集的难读、难编、难记和易出错的缺点,后来就出现了面向特定CPU的特定汇编语言,比如打上这样的x86汇编指令mov
、ax
,然后用上用机器码做的汇编器,它将会被翻译成 1000100111011000 这样的二进制01格式的机器指令。
汇编语言执行过程如图所示。
汇编语言的特点是:相对于机器指令而言,代码编写较为简单,程序被赋予了语义,对程序员较为友好,代码需要借助汇编器转换为机器指令,效率比机器指令略低。
汇编的实质是机器指令(机器码)的助记符,是一种低级符号语言,机器指令集是一款CPU的编程特征,是这款CPU的设计者制定的,CPU的内部电路设计就是为了实现这些指令集的功能。每种CPU架构都有自己的指令集,这些指令集决定了CPU能够执行哪些操作以及如何执行这些操作。因此,不同的CPU架构会有不同的汇编语法和编译器。另外,由于汇编语言仍是面向机器的语言,因此在使用汇编语言编程前仍然需要具备一定的计算机指令集的基础才能正确的编写出汇编语言。
CPU常见的架构有:arm架构,x86架构,mips架构等,汇编语言是针对某一个CPU而写的,不能编译到另一个CPU。例如: add eax,ebx
就只是X86 汇编的。而mips的,可能是add $t0,$t0,$t1
。
不同CPU架构上的汇编语言指令不同,不同操作系统之间的指令也可能不一样,而为了统一一套写法,同时又不失汇编的表达能力,因此高级编程语言就诞生了。
2.1.3 高级语言
用高级语言写的代码(以C语言举例),首先会被高级语言的编译器先编译成当前符合CPU架构的汇编指令(这需要借助指定的编译器),然后再将该汇编指令转成CPU能识别的机器码,最终交由计算机解释执行。
高级语言执行过程如图所示。
有了高级语言我们就不需要去阅读特定CPU的汇编码,只需要写通用的高级语言的源代码就可以实现程序的编写,借助高级语言的编译器就可以实现跨CPU的代码编写。
我们用将更偏机器实现的汇编语言称为低级语言,与汇编相比之上的语言,我们称之为高级语言。
机器语言、汇编语言、高级语言三者的对比如表所示。
对比维度 | 机器语言 | 汇编语言 | 高级语言 |
---|---|---|---|
设计目的 | 直接与计算机硬件交互 | 简化机器语言编程 | 接近自然语言编程 |
语法复杂度 | 高(二进制代码) | 中(符号化的机器指令) | 低(接近自然语言) |
执行效率 | 最高(直接执行) | 较高(需汇编成机器语言) | 较低(需编译成机器语言) |
可读性 | 低 | 中 | 高 |
可移植性 | 低 | 中 | 高 |
直接执行性 | 是 | 否 | 否 |
需要翻译 | 否 | 是 | 是 |
示例语言 | 无特定名称 | 汇编语言(assembly) | Java、C/C++、Python、Go、VB等 |
适用场景 | 直接控制硬件 | 优化机器语言程序 | 大多数编程任务 |
2.1.4 语言的跨平台特性
不管是高级语言还是汇编语言最终将被翻译成0101这样的机器指令交由CPU执行,不同的CPU架构解释的机器指令是不一样的。假设我们利用汇编指令编写好了一个基于x86架构的CPU的汇编程序,那么该汇编程序就无法在使用了arm架构的CPU的计算机上执行。那么如何统一一套写法可以在不同的CPU架构的计算机上执行呢?这就是我们要考虑的语言的跨平台特性。即程序代码只编写一次,但可以在不同的平台(CPU的架构)执行该程序。
不难发现,高级语言(例如C)就是具备了跨平台的特性。假设我们使用C语言编写好了一个hello程序,我们需要安装好符合当前CPU架构的编译器,例如x86的编译器,然后将C语言编写的hello程序编译成符合x86架构的汇编指令,最后由汇编器编译成x86架构的机器指令执行。如果需要将该hello程序运行在arm架构的CPU上,我们只需要安装arm架构的编译器,将该hello程序的源代码编译成符合arm架构的汇编指令,最终再由汇编器编译成arm架构的机器指令即可。
高级语言跨CPU的实现如图所示。
实际上,高级语言(例如C)在编译成汇编指令或执行该汇编指令时,编译器与指令需要与操作系统做交互,不同的操作系统提供的系统API、系统的资源管理等都会有所不同,因此高级语言在编译成汇编时不仅要考虑CPU架构(该编译器要符合CPU架构),还要考虑操作系统的因素。
上面的案例中并没有考虑到操作系统的因素。实际上,C语言编写的源代码程序想要在windows平台运行还要考虑到操作系统的因素,那么完整的说法应该是需要安装windows平台基于x86架构的编译器,才能在使用了x86架构的CPU并安装了windows操作系统的计算机上运行该程序,如图所示。
这样一看是不是太混乱了?想要做到跨平台也太麻烦了吧?如果CPU的架构再多几个,操作系统的平台再多几个那么对应的编译器岂不是变得非常多?从原则上来说是的,但是我们要了解一下市场行情就知道我们所担心的问题其实并不会出现。
研发一款新架构的CPU并不是一件容易的事情,当前市场上主要的CPU架构只有x86架构和ARM架构(x64架构基于x86架构)。x86架构是由Intel和AMD两家公司主导的个人电脑和服务器市场的架构,而ARM架构则主要用于移动设备如智能手机和平板电脑。
操作系统的个人PC主要份额都集中在windows,以及一小部分的Linux和Mac等。所以官方在推出编译器时也会考虑目前操作系统的市场环境,例如很多语言的编译器/解析器/运行平台等在Linux平台会推出x64架构以及arm架构,但是在windows平台就只会推出x64架构的编译器,这主要还是市场环境决定的。
Tips:x86是指intel早期的开发的一种32位指令集,所有intel和amd早期的cpu都支持这种指令集,32位指令集的CPU有非常多的局限性,因此厂商们向着64位指令集的CPU进军。早年,amd比Intel率先制造出了商用的兼容x86的CPU,amd称之为amd64,后来Intel也支持amd64的指令集,但是换了个名字,叫x86_64。实际上,x86_64,x64,AMD64基本上是同一个东西,我们现在用的intel/amd的桌面级CPU基本上都是x86_64,并且大都兼容x86。x86、x86_64主要的区别就是32位和64位的问题,x86中只有8个32位通用寄存器,x86_64把这8个通用寄存器扩展成了64位的,并且比x86增加了若干个寄存器。
arm架构是源自于Acorn计算机公司1983年开始开发一颗主要用于路由器的处理器,arm架构非常适用于移动通信这种低成本,高性能,低耗电的领域。另外arm架构也分为32位与64位,包括AArch32、AArch64等,一般情况下64位都会向下兼容32位指令集。
通常来说,CPU的指令集也决定了操作系统的位数,例如x86架构的CPU只能安装32位操作系统,x64架构的CPU能够安装32位和64位的操作系统。就目前来说,64位操作系统已经成为了绝大多数人的选择,厂商的CPU指令集也大多都是x64架构的。
目前所提倡的面向服务架构、云计算、大数据等通过网络使得软件运营商只要给用户提供客户端的操作界面即可,数据的计算、维护等都交由服务器完成,数据之间的交互没有了编程语言、操作系统、硬件架构的限制。