数组
前面说了在C++中是通过变量来对内存进行访问的,但根据前面的说明,C++中只能通过变量来操作内存,也就是说要操作某块内存,就必须先将这块内存的首地址和一个变量名绑定起来,这是很糟糕的。比如有100块内存用以记录100个工人的工资,现在要将每个工人的工资增加5%,为了知道各个工人增加了后的工资为多少,就定义一个变量float a1;,用其记录第1个工人的工资,然后执行语句a1 += a1 * 0.05f;,则a1里就是增加后的工资。由于是100个工人,所以就必须有100个变量,分别记录100个工资。因此上面的赋值语句就需要有100条,每条仅仅变量名不一样。
上面需要手工重复书写变量定义语句float a1;100遍(每次变一个变量名),无谓的工作。因此想到一次向操作系统申请100*4=400个字节的连续内存,那么要给第i个工人修改工资,只需从首地址开始加上4*i个字节就行了(因为float占用4个字节)。
为了提供这个功能,C++提出了一种类型——数组。数组即一组数字,其中的各个数字称作相应数组的元素,各元素的大小一定相等(因为数组中的元素是靠固定的偏移来标识的),即数组表示一组相同类型的数字,其在内存中一定是连续存放的。在定义变量时,要表示某个变量是数组类型时,在变量名的后面加上方括号,在方括号中指明欲申请的数组元素个数,以分号结束。因此上面的记录100个工资的变量,即可如下定义成数组类型的变量:
float a[100];
上面定义了一个变量a,分配了100*4=400个字节的连续内存(因为一个float元素占用4个字节),然后将其首地址和变量名a相绑定。而变量a的类型就被称作具有100个float类型元素的数组。即将如下解释变量a所对应内存中的内容(类型就是如何解释内存的内容):a所对应的地址标识的内存是一块连续内存的首地址,这块连续内存的大小刚好能容纳下100个float类型的数字。
因此可以将前面的float b;这种定义看成是定义了一个元素的float数组变量b.而为了能够访问数组中的某个元素,在变量名后接方括号,方括号中放一数字,数字必须是非浮点数,即使用二进制原码或补码进行表示的数字。如a[ 5 + 3 ] += 32;就是数组变量a的第5 + 3个元素的值增加32.又:
long c = 23; float b = a[ ( c – 3 ) / 5 ] + 10, d = a[ c – 23 ];
上面的b的值就为数组变量a的第4个元素的值加10,而d的值就为数组变量a的第0个元素的值。即C++的数组中的元素是以0为基本序号来记数的,即a[0]实际代表的是数组变量a中的第一个元素的值,而之所以是0,表示a所对应的地址加上0*4后得到的地址就为第一个元素的地址。
应该注意不能这样写:long a[0];,定义0个元素的数组是无意义的,编译器将报错,不过在结构或类或联合中符合某些规则后可以这样写,那是C语言时代提出的一种实现结构类型的长度可变的技术,在《C++从零开始(九)》中将说明。
还应注意上面在定义数组时不能在方括号内写变量,即long b = 10; float a[ b ];是错误的,因为编译此代码时,无法知道变量b的值为多少,进而无法分配内存。可是前面明明已经写了b = 10;,为什么还说不知道b的值?那是因为无法知道b所对应的地址是多少。因为编译器编译时只是将b和一个偏移进行了绑定,并不是真正的地址,即b所对应的可能是Base - 54,而其中的Base就是在程序一开始执行时动态向操作系统申请的大块内存的尾地址,因为其可能变化,故无法得知b实际对应的地址(实际在Windows平台下,由于虚拟地址空间的运用,是可以得到实际对应的虚拟地址,但依旧不是实际地址,故无法编译时期知道某变量的值)。
但是编译器仍然可以根据前面的long b = 10;而推出Base - 54的值为10啊?重点就是编译器看到long b = 10;时,只是知道要生成一条指令,此指令将10放入Base - 54的内存中,其它将不再过问(也没必要过问),故即使才写了long b = 10;编译器也无法得知b的值。
上面说数组是一种类型,其实并不准确,实际应为——数组是一种类型修饰符,其定义了一种类型修饰规则。关于类型修饰符,后面将详述。
字符串
在《C++从零开始(二)》中已经说过,要查某个字符对应的ASCII码,通过在这个字符的两侧加上单引号,如'A'就等同于65.而要表示多个字符时,就使用双引号括起来,如:"ABC".而为了记录字符,就需要记录下其对应的ASCII码,而ASCII码的数值在-128到127以内,因此使用一个char变量就可以记录一个ASCII码,而为了记录"ABC",就很正常地使用一个char的数组来记录。如下:
char a = 'A'; char b[10]; b[0] = 'A'; b[1] = 'B'; b[2] = 'C';
上面a的值为65,b[0]的值为65,b[1]为66,b[2]为67.因为b为一个10元素的数组,在这其记录了一个3个字符长度的字符串,但是当得到b的地址时,如何知道其第几个元素才是有效的字符?如上面的b[4]就没有赋值,那如何知道b[4]不应该被解释为字符?可以如下,从第0个元素开始依次检查每个char元素的值,直到遇到某个char元素的值为0(因为ASCII码表中0没有对应的字符),则其前面的所有的元素都认为是应该用ASCII码表来解释的字符。故还应b[3] = 0;以表示字符串的结束。
上面的规则被广泛运用,C运行时期库中提供的所有有关字符串的操作都是基于上面的规则来解释字符串的(关于C运行时期库,可参考《C++从零开始(十九)》)。但上面为了记录一个字符串,显得烦琐了点,字符串有多长就需要写几个赋值语句,而且还需要将末尾的元素赋值为0,如果搞忘则问题严重。对于此,C++强制提供了一种简写方式,如下:
char b[10] = "ABC";