学习一门程序设计语言,首先需要对计算机的基本工作原理有一定的了解。因为语言只是一门工具,由语言开发出来的程序最终都必须在机器上运行,对机器工作原理的理解,有助于编写正确高效的代码。正如我们学习汽车驾驶并不需要使驾驶员成为汽车专家,但了解汽车的工作原理,对我们正确驾驶汽车很有帮助。
一、计算机的基本组成
计算机由三大子系统组成:中央处理单元、存储器、输入输出系统。通过总线将这些子系统连接起来,其示意图,如图1所示。
图1 计算机组成示意图
1.存储器
存储器用于存取数据和程序。这里所说的存储器,并不是广义的存储体系,而是指计算机的内部存储器,也称内存、主存。我们通常所说的硬盘,也是一种存储设备,归属计算机的外部设备,请注意两者的区别。
计算机在运行时,需要先把程序和数据读取到内存中,程序对应于指令序列,计算机按顺序执行指令。这些信息并不是一直存在于内存中的,通常存放在磁盘这样的外设中。因为磁盘中的信息断电后并不会消失,而内存中的信息在关机后就会消失。这也是我们在电脑上做某些工作,需要定期保存结果的原因。而我们安装机器系统的时候,数据也都是写到磁盘上的。在机器运行过程中,需要实现什么功能,就把对应的程序读入到内存中,那么为什么,不把所有的内容一次性地都读入到内存中呢?
有如下两个原因:
①内存和磁盘在存储空间、访问速度和价格上的差异
目前内存的大小通常是2GB、4GB(G表示230 ,B表示字节,一个字节表示8个二进制位),磁盘的大小已达TB数量级(T表示240)。而内存的平均访问时间为100 ns,硬盘为10,000,000 ns。总之,内存更快,价格更高,但容量有限。
②程序执行的局部性原理
程序总是趋向于使用最近使用过的数据和指令,访问的存储器地址分布,并不是随机的。也就是说,并没有必要把所有的数据和指令都装入到内存中,哪怕是内存足够便宜也足够大,这样对程序的执行速度也不会有更大的提升。
请思考一下,为什么在CPU里还有缓存体系?
衡量主存的指标主要是存储容量和访问速度。用于表示存储的单元见表1。
表1 存储单位
存储器又可分为RAM和ROM。RAM表示随机存储器,就是我们平时所说的内存条,通常以插卡的方式和机器主板连接。ROM表示只读存储器,其内容是制造商写入的,用户只能读,通常用于存储那些开机时运行的程序。
内存通常是按字节为单位编址的,每个存储单元都有一个唯一的标识符,称为“内存地址”。尽管在程序中可以使用名字来表示内存区域,但最终都要转换成硬件能识别的地址,才能访问该名字所表示的内存空间。
2.中央处理器
中央处理器也称CPU(Central Processing Unit)。它相当于人的大脑,用于数据的计算处理工作。通常CPU由算术逻辑单元、控制单元和寄存器组成。
计算机求解问题是通过程序来实现的,程序员编写好代码后(也被称为源代码),通常不能直接执行,而需要某种工具软件将它转换成机器能识别的指令(机器码)。每个具体型号的CPU都有对应的指令集。机器码级别的程序是不能随便拿到不同的机器上用的,因为硬件平台不同,这也是程序员所要面临的问题之一:可移植性。
控制单元用于负责对指令进行译码,并发出对应的控制信号。
算术逻辑单元用于对数据的逻辑、移位和算术运算。
寄存器拥有非常高的读写速度,是用于临时存放数据的高速存储单元。
衡量CPU性能的关键指标是主频,也就是主时钟频率,单位为Hz。主频越快,其运算速度也越快。目前CPU的主频通常为几GHz,在台式计算机上,单纯提高主频越来越困难,CPU的多核化发展趋势很明显。
如何查看机器的CPU和内存信息?在Windows 7.0环境下,用鼠标右键单击桌面上的“计算机”图标,在弹出的快捷菜单中选中“属性”。图2是本机的处理器和内存信息截图,请尝试刚学到的知识对这些信息进行解读。
图2 计算机CPU和内存信息
3.输入/输出系统(I/O)
输入/输出系统(I/O)如键盘、显示器、鼠标、打印机、磁盘等。通过这些输入/输出设备,实现计算机与外界通信。
4.总线和控制器
在CPU和内存间通常由总线连接起来,包括数据总线、地址总线和控制总线。而输入输出设备,因为在速度和电气性能上的差异并不是直接和连接在这些总线上的,而是通过各种控制器连接到总线上的。这些控制器,也就是通常所说的接口。一个控制器,可以连接多个外部设备,这样可以很方便地扩展机器的功能。常用的计算机接口有SCSI接口、IEEE1394接口(也称“火线”)和USB接口。
二、数据在计算机内的表示和存储
我们都听说过,计算机是采用二进制的,所有的信息都被表示成二进制的形式。在人的现实生活中,除了要用到数值数据(如身高、年龄)外,还有文字数据(如名字、说过的某一句话)和大量的多媒体信息(如声音、视频)。试想,如何用二进制的方式来表示这些信息呢?
方法就是,对这些信息进行分类并编码。
可以定义基本的数据类型:整数、实数和字符。整数和实数都是无限集合,用状态有限的计算机是不能完全表示的。因此,需要做出取舍,用多少个二进制位来表示一种数据类型,然后再考虑如何进行编码。
1.整数编码
(1)无符号整数的编码
假设用4bit(四个二进制的位)来表示整数。二进制只有两个符号“0”和“1”,每一个二进制的位或者为“0”或者为“1”。长度为4的二进制位串共有16(24)个,见表2的第一列。按与十进制计数体系类似的方式来理解,如十进制的1024,可展开为多项式:1024 = 1×103+ 0×102 + 2×101 + 4×100。
表2 无符号整数的编码
续表
采用这种方式编码,没有包含负整数,即这只是“无符号整数”编码。其表示的整数范围是0~15。依此类推,目前32位的机器采用32bit来表示整数,如何用来表示无符号整数,能表示的范围是多少?在C语言中,用关键字unsigned int声明无符号整数,而用int表示有符号整数。
(2)有符号整数的编码
对有符号整数,需要用一个二进制位表示符号位,如果取最高位(最左边的位)作符号位,并且人为规定最高位为0表示正数,为1表示负数。那么,最直观的编码方式是,用剩下的位表示整数的绝对值大小。则位串“1111”表示-7,而0会有两种编码表示“0000”和“1000”,因此,计算机采用另外一种编码来表示有符号整数,即“二进制补码”。
先通过一个例子来解释补码的原理:比如我们熟悉的时钟,共12个刻度。如果按无符号整数来理解,每个刻度分别对应于0点、1点、2点…11点。如果按有符号整数来理解,将11点钟方向理解成-1点,并依此类推,这是一个有符号的时钟,如图3所示。
也许有人会问:为什么不能把6点钟方向标为“-1”,而要把11点钟方向标为“-1”呢?原因是这样编码,更适合计算机进行数据的运算,可以将减法运算用加法的方式来处理。以这个时钟为例,“加法”相当于顺时针拨动指针,“减法”对应于逆时针拨动指针,采用这种“补码”编码方式,就可以只保留“加法”操作。即逆时针移动1个刻度,可以用顺时针移动11个刻度来实现。如:
2–1=2+11=13(正好对应于1)
图3 有符号的时钟
以这个直观的例子为基础,再来解释计算机如何存储有符号整数。
先介绍两种运算:反码运算和补码运算。反码运算表示按位取反。而补码运算:从低位复制位,直到有1被复制;然后,反转其余的位。
例如:计算整数01101010的反码和补码
很显然,对一个位串进行两次反码运算,可以得到原来的数。
同样,对一个位串进行两次补码运算,也可以得到原来的数。
计算机存储有符号的整数,遵循如下步骤:
①先将整数的值变成二进制数。
②如果是正数,原样存储;如果是负数,计算补码并存储。
例如:整数-1,用补码方式存储在8位的存储单元。
从二进制补码格式还原整数,计算机遵循的步骤如下:
①最高位为1,计算机取其补码;最高位为0,不进行操作。
②将该二进制数转换为十进制数。
例如,已知机器内的二进制位串为:10010110,最高位为“1”,它是一个负数,求补得到11101001为数值的大小,因此,该补码表示的是十进制数-105。
下面看看,在二进制补码表示整数的基础上,减法运算是如何转换成加法运算的。两个整数A、B,都用二进制补码表示,则A减B和A加B的补码得到的结果是一样的。例如:
再来看看补码10100000表示哪个整数,如果表示-95,那这个计算结果就是正确的。按前面所说的规则,最高位为1,表示是一个负数,求码得到该负数的大小:(www.xing528.com)
请自己分析一下10-(-105)的计算过程。
计算机看到一个二进制的位串,必须了解其确切类型,才能对它做正确的解析和进一步的操作。表3就是整数在计算机内的表示。自己分析一下,在32位的机器里,用32位来表示有符号整数和无符号整数,表示的数据范围是什么?
表3 整数的编码
2.实数编码
实数是带有小数的,用一定的宽度来表示实数,有两种方法:小数点的位置是固定的,或者小数点的位置是浮动的。例如:
如果规定用8个数码宽度来表示实数,规定小数部分占两个数码,那么1.3415就只能表示成1.34,精度受损。
很显然,带有很大整数部分或者很小小数部分的实数是不适合用定点数来表示的。
浮点表示法允许小数点的位置浮动:小数点左右可以有不同数量的数码。这样增大了可表示的实数范围。可以用科学计数法来表示小数135600000000000.00
只需要记住3个部分:符号、位移量(指数部分)及定点部分。这样更节省空间。
对于二进制表示的实数, -(0.000000000001101)2可以用同样的方法来表示:
可以看出,采用浮点数的方式,可以表示的实数范围大大增加了。那实数如何在机器内表示的呢?
(2)单精度和双精度浮点数
IEEE(电气和电子工程师协会)定义了几种浮点数的标准。其中包括单精度的浮点数和双精度的浮点数。下面以单精度数为例加以说明。
单精度数格式是用32位来存储一个浮点数。符号位占用1位(0为正,1为负),指数占8位,尾数占有23位,如图4所示。
图4 单精度浮点数的格式
以-(0.000000000001101)2为例说明单精度浮点数的存储方式。
尾数:底数(1.101)2中小数点前始终为1,故只需要存储小数点右边的位,也被称为尾数。不足23位,在右边补0。
指数:以2为底的指数,指数是有符号的,但并没有使用补码来表示指数,而是采用“余码”的方式来存储。采用这种方式,8位表示的有符号整数的范围为-127~128,在存储时,加上偏移量27-1=127。这样的好处是,余码系统中,所有的整数都是正数。需要注意的是,指数全为0或全为1有特殊的意义。
符号位,负数,该位置1。故单精度数-(0.000000000001101)2在计算机内的存储,如图5所示。
图5 单精度浮点数的存储示例
对单精度浮点数的解析过程,就是以上存储计算过程的逆过程,请自行分析。下面粗略分析一下单精度数的精度和表示范围。
指数部分为8位,单精度数表示的范围约为:-2128~2128。1.17E-38~3.4E+38(科学计数法)。
尾数部分为23位,223=8388608,即有效数字6~7位,可以保证6位有效数字。
双精度数的表示方法与单精度数的类似,其指数部分占11位、尾数部分占52位,请分析双精度数的表示范围和有效数字位数。
3.字符编码
不同的语言,通常对应不同的字符。问题是:在一种语言中,到底需要多少位来表示一处符号?如何能设计一个字符编码,能够兼容多种语言?
(1)ASCII码
ASCII码,是美国标准信息交换码的简称。使用7位表示一个符号,该字符集定义了128种符号,如表4所示。
表4 ASCII码表
续表
需要注意以下几点:
①表中用十进制数表示ASCII值。括号内的符号^代表Ctrl键。
②该字符集包含大小写字母、数字、标点符号外,还包含一些控制字符。且大小写字母是分别编码的。
③每个字符对应于一个ASCII码值,字符间是可以根据这个值比较大小的,表示它们在表中的先后位置关系。
④并不是每个ASCII码字符都是可显示的和可直接由键盘输入的,对控制字符的输入要采用特殊的机制。
⑤常用的控制字符如表5所示。
表5 常用的控制字符表
(2)UNICODE编码
UNICODE编码又称万国码,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。代码中的不同部分被分配用于来自世界上不同语言的符号,其中还有些部分用于表示图形和特殊的符号。
三、软件与程序设计语言
1.操作系统介绍
计算机由硬件和软件组成,硬件是物理设备,而软件是使计算机能正常工作的程序集合。计算机软件分两大类:操作系统和应用软件。
操作系统是一个非常庞大的软件系统,从功能上讲,它是计算机硬件与用户的一个接口,它使其他程序更加方便有效的运行,并能方便对计算机硬件和软件资源进行访问。即操作系统是应用软件和硬件之间的中间层,它负责有效地利用硬件资源,对应用软件提供一个方便的接口,便于用户访问系统资源。
计算机软件通常采用分层结构,层与层之间定义好接口,如图6所示。这种结构的好处是,只要保持层与层之间的接口不变,每个层都可以很方便地修改和替换。下层为上层提供某种服务,每层只负责有限的功能,这样就可以构建一个较大的软件系统。如Internet网络互连的软件就是采用这种分层结构的典型例子。
图6 软件的层次结构
目前在个人计算机领域,主流操作系统包括Windows、Unix、Linux、Mac OS。这些操作系统都支持多用户、多任务,而且在提供方便的图形界面的同时,也支持命令行界面访问方式。操作系统属于软件,有源代码,可以分为开源操作系统和闭源操作系统。
操作系统通常包含内存管理、进程管理、设备管理、文件管理四个部分。
操作系统提供给用户程序的服务也被称为系统调用,在Windows中也被称为API(应用程序接口)。如果我们要实现一个简单的C语言程序,往屏幕上输入一个字符串。最方便的方法是调用一个标准库函数printf(),这个函数在运行时,也会进行系统调用。
2.程序设计语言
(1)机器语言:计算机发展的早期,只能用机器语言,进行程序开发。所谓机器语言,是直接由“0”和“1”组成的。程序员需要了解硬件的指令编码格式,手工生成机器级的指令,这样很容易犯错,而且效率十分低下。而不同硬件通常对应不同的指令集,程序的移植性也无从谈起。
(2)汇编语言:正因为机器语言的这些缺点,人们开始思考:能不能用一种更好记忆的符号来表示机器指令,然后通过一个程序将由这些符号组成的程序翻译成机器指令?由此产生了第二代程序设计语言,也被称为汇编语言。这个翻译程序也被称为汇编器。
(3)高级语言:尽管汇编语言大大地提高了编程效率,但由于汇编语言和硬件关联过于紧密,程序员不得不将很大的精力用于汇编语言的繁琐细节。为了使程序员将精力集中到应用程序本身,而不是计算机的复杂性上,出现了高级语言,如:BASIC、C、C++、Java等。程序语言的示例,如图7所示。
图7 程序语言示例
高级语言编写的程序,可读性更强,能使程序员更专注于求解的问题。但这种源程序是不可直接运行的,它也需要由专门的程序翻译成机器指令。翻译方法,可分为编译和解释,与此对应,翻译程序又被称为编译器和解释器。
直观地理解,编译是先将整个源程序翻译成目标程序(可执行的),然后装入目标程序并运行。而解释就是每次将源程序的某一行翻译成机器语言指令,并执行,然后翻译并执行下一行(Java除外:Java是先编译成字节码,然后由目标机上的Java虚拟机解释执行)。
高级语言通常都有一些共同的概念,如:标识符,数据类型,运算符,变量,常量,表达式,语句,函数(或方法)。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。