1.2.1 位、字节、字的概念
在主机内的存储器,称内存储器,简称内存。要运行的程序和数据都存放在内存中。那么内存是由什么组成的呢?它原来是由千千万万个具有二个状态的电子开关组成的。电子开关打开时的状态为“1”,闭合时的状态为“0”。每个电子开关用计算机术语“位(bit)”来称呼,它可以代表二进制数(逢二进一)的一个基本单元。计算机内存就是一个庞大的二进制基本单元——电子开关的集合体。
要存放信息,必须把这些电子开关有机地组织起来,一般用若干个二进制“位”组成一个“字节”(byte),多数计算机用8位组成一个字节,如图1.1所示。
一个字节可以存放一个字符,当然这个字符是用一个二进制数来表示的,这个二进制数就是这个字符的二进制代码。计算机上所应用的全部字符(字母、数字或其他专用字符)都有相应的二进制代码,如字母“a”,它的二进制代码是:
这些字符和二进制代码之间的对应关系,很多计算机系统采用ASCII(American Standard Code for Information Interchange即“美国标准信息交换码”)代码。这个代码与字符之间的对应关系见附录I。
计算机内存就是由很多排列整齐的字节组成,为了管理方便,每个字节都有相应的位置编号,这个编号就是这个字节的“地址”,通过地址可以找到内存中任何一个字节的内容。
一个字节可以存放一个字符,但要存放一个整数或一个实数,一个字节就不够了,有的系统上整数用2个字节或4个字节来存放,而实数用4个字节来存放。由一个或若干个字节组成一个“字”,一个字可以用来存放一个数据或一条指令。
1.2.2 内存单元、内存单元地址和指针的概念
一个内存单元可以用来存放一个数据或一条指令,由于内存中每个字节都有自己的地址,因此每个内存单元也有自己相应的地址,由一个字节组成的内存单元的地址,就是这个字节的地址,而由多个字节组成的内存单元的地址则指的是组成这个内存单元几个字节中第一个字节的地址。假定程序中已定义了三个短整型变量i、j、z,编译时系统分配给它们各一个内存单元,每个内存单元占两个字节,如图1.2所示,图中每个长方形框代表两个字节。如果分配给变量i的内存单元地址是5000(这只是假定的地址),那么变量i占用地址为5000开始的两个字节(即地址为5000和5001两个字节),5000是变量i的内存单元地址,简称变量i的地址,C/C++语言中用符号“&i”表示,这里&表示取地址,&i的值是5000。若定义变量时j是紧跟在变量i之后,变量z又紧跟在j之后,那么系统分配给这些变量的内存单元一般是连续的,即分配给变量j的内存单元地址是5002,分配给z的内存单元地址是5004,所以变量j的地址&j的值是5002(变量j占用地址为5002和5003两个字节),而变量z的地址&z的值为5004(变量z占用地址为5004和5005两个字节),所以,当程序定义了某一变量以后,变量名和它占用的内存单元地址有直接的对应关系。
请注意,要区别一个内存单元的地址和这个内存单元的内容(即变量值)这两个概念。对变量值的存取是通过地址来进行的,根据变量名和地址的对应关系找到变量的地址,然后从这个地址所标识的存储单元中进行存取数据的操作。图1.2中,设变量i,j的值分别是5和3,要执行一个操作z=i+j时,过程是这样的:从变量i存储单元的地址5000开始的两个字节中取出变量i的值5,再从变量j存储单元的地址5002开始的两个字节中取出变量j的值3,通过CPU将它们相加后得8送到变量z内存单元中,即送到地址为5004开始的两个字节中。这种按变量地址存取变量的方式称为“直接访问”(即:直接寻址)方式,这里的“访问”指的是在内存单元中存取(或称读写)数据的意思。
总之,通过变量的地址能找到变量的内存单元,因此,把变量的地址称作变量的“指针”,如地址5000是变量i的指针,地址5002是变量j的指针。通过变量的指针可以找到变量的内存单元来对变量进行读写操作。指针指的是某个内存单元的首地址(或说一个内存单元的入口地址),因此C语言中应用的指针是有数据类型的(详见第2章),而每一种数据类型的变量在内存中存放的数据是以内存单元为单位的。
与“直接访问”相对应的另一种访问内存的方式是“间接访问”(即:间接寻址)方式。C语言中可以定义一种变量专门用来存放地址,假定我们定义一个变量i_pointer是用来存放整型变量地址的,系统编译时分配给这个变量的地址假定是6020,那么可以通过下面赋值语句将变量i的地址赋给变量i_pointer:
i_pointer=&i ;
变量i的地址值是5000,所以通过以上赋值语句就把地址值5000放入地址是6020开始的两个字节中,i_pointer的值就是5000。这样,我们就可以对变量i进行间接访问了,过程是:先从变量i_pointer中提取变量i的指针5000,然后到地址是5000和5001两个字节中存取i的值。图1.3表示直接访问和间接访问的示意图。
图1.3(a)表示对变量i的值直接进行读写操作(直接访问);图1.3(b)表示对i_pointer所“指向”的内存单元进行读写操作(间接访问)。因为i_pointer的值是变量i的地址,所以称i_pointer指向变量i,借助于i_pointer可以对变量i进行读写操作。当i_pointer的值用变量j的地址&j重新赋值,那么借助于i_pointer可以对变量j进行读写操作。因为i_pointer的值可以改变,所以称它为“指针变量”。关于指针变量的定义详见第2章,这里只作概述,目的是说明借助于它访问内存的方式(间接访问的方式)。
1.2.3 原码、反码和补码的概念
内存中的每一个数都占用一个内存单元,而这个内存单元中的最高位用来表征数的符号,以0代表正数,以1代表负数,其余各位代表数的绝对值。
一个数的代码有原码、反码和补码三种表达方式。而正数的原码、反码和补码的形式都相同,没什么差别,如一个短整数,在内存中占两个字节,假定它们在内存中是并排的,则+8的原码、反码和补码都具有如下的形式:
请读者注意,负数的原码、反码和补码的表示形式是不同的,如-8的原码是:
负数的反码规定:符号位不动,其余各位对原码取反。如-8的反码是:
负数的补码规定:符号位不动,其余各位对原码取反,然后加1。也就是说,一个负数的补码是它的反码加1。如-8的补码是: