现代C++编程:从入门到实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.2 数组

数组是相同类型变量的序列。数组的类型包括它所包含的元素的类型和数量。在声明语法中可以把这些信息组织在一起:元素类型在方括号前面,数组大小在方括号中间。

例如,下面的代码声明了一个包含100个int对象的数组:

2.2.1 数组初始化

有一种使用大括号初始化数组值的快捷方式:

我们可以省略数组的长度,因为它可以在编译时从大括号中的元素数量推断出来。

2.2.2 访问数组元素

使用方括号包围所需的索引即可访问数组元素。在C++中,数组的索引是从零开始的,所以第一个元素的索引是0,第十个元素的索引是9,以此类推。代码清单2-9说明了读写数组元素的方法。

代码清单2-9 一个索引数组的程序

这段代码声明了一个名为arr的四元素数组,包含元素1、2、3和4❶。在下一行❷,它打印了第三个元素。然后,它将第三个元素赋值为100❸,所以当重新打印第三个元素❹时,其值是100。

2.2.3 for循环简介

for循环可以重复(或迭代)执行某些语句特定次数。我们可以规定一个起点和其他条件。init-statement(初始化语句)在第一次迭代之前执行,所以它可以初始化for循环中使用的变量。conditional是一个表达式,在每次迭代前被求值。如果它被评估为true,迭代继续进行。如果为falsefor循环就会终止。iteration-statement在每次迭代后执行,这在必须递增变量以覆盖一个数值范围的情况下很有用。for循环的语法如下:

例如,代码清单2-10显示了如何使用for循环来寻找数组的最大值。

代码清单2-10 寻找数组中包含的最大值

首先把maximum初始化为可能的最小值❶;这里是0,因为它是无符号的。接下来,初始化数组的值❷,用for循环❸遍历这些值。迭代器变量i的范围从04(包括4)。在for循环中,访问数组values的每个元素,并检查该元素是否大于当前的maximum❹。如果大于,就把maximum设置为新的值❺。循环结束后,maximum等于数组中的最大值,打印出maximum的值❻。

注意 如果你以前用C或C++编程过,你可能想知道为什么代码清单2-10采用size_t而不是int作为i的类型。这是因为考虑到values理论上可以占用最大可用存储。size_t可以保证在数组内部对任何值进行索引,int却不能。在实践中,这其实没有区别,但从技术上讲,size_t是正确的。

1.基于范围的for循环

代码清单2-10展示了如何使用for循环❸来迭代数组中的元素。我们可以通过基于范围(range-based)的for循环来消除迭代器变量i。对于像数组这样的特定对象,for知道如何在对象中的值的范围内进行迭代。下面是基于范围的for循环的语法:

声明迭代器变量element-name❷的类型为element-type❶。element-type必须与要迭代的数组内的元素类型相匹配。要迭代的数组就是array-name❸。

代码清单2-11用基于范围的for循环重构了代码清单2-10。

代码清单2-11 用基于范围的for循环重构代码清单2-10

注意 第7章将介绍表达式的知识。现在,暂且把表达式看作对程序产生影响的一些代码。

代码清单2-11大大改进了代码清单2-10。一目了然,一看就知道for循环迭代了数组values❶。因为抛弃了迭代器变量i,所以for循环的主体得到了简化,可以直接使用values的每个元素❷❸。

请善用基于范围的for循环。

2.数组中元素的数量

使用sizeof运算符可以获得数组的总大小(以字节为单位)。我们可以使用一个简单的技巧来确定数组的元素数:用数组的大小除以单个元素的大小。

在大多数系统上,sizeof(array)❶将计算为16字节,sizeof(short)❷将计算为2字节。不管short的大小如何,n_elements总是初始化为8,因为因子会抵消。这个计算发生在编译时,所以以这种方式评估数组的长度没有运行时成本。

sizeof(x)/sizeof(y)构造太过于偏重技巧,但它被广泛用于旧代码中。第二部分将探讨其他存储数据的方法,这些方法不需要对数据长度进行外部计算。如果必须使用数组,则可以使用<iterator>头文件中的std::size函数安全地获得元素的数量。

注意 好的是,std::size可以与任何暴露了size方法的容器(见第13章)一起使用。这在编写泛型代码(见第6章)时特别有用。此外,如果不小心传递了一个不支持的类型,如指针,它将拒绝编译。

2.2.4 C风格字符串

字符串是由字符组成的连续序列。C风格的字符串或null结尾字符串会在末尾附加一个零(null),以表示字符串结束了。因为数组元素是连续的,所以我们可以在字符类型的数组中存储字符串。

1.字符串字面量

用引号("")括住文本即可声明字符串字面量。像字符字面量一样,字符串字面量也支持Unicode:只要在前面加上适当的前缀,如L。以下示例将字符串字面量赋给englishchinese数组:

注意 其实,我们一直都在使用字符串字面量:printf语句的格式化字符串便是字符串字面量。

这段代码产生了两个变量:english(包含A book holds a house of gold.)和chinese(包含“书中自有黄金屋”的Unicode字符)。

2.格式指定符

窄字符串(char*)的格式指定符是%s。例如,可以将字符串纳入格式化字符串,如下所示:

注意 将Unicode打印到控制台出乎意料得复杂。通常情况下,我们需要确保选择了正确的代码页,而这个话题远远超出了本书的范围。如果需要将Unicode字符嵌入字符串字面量,请看<cwchar>头文件中的wprintf

连续的字符串字词会被串联在一起,任何中间的空白或换行符都会被忽略。因此,可以在源代码中将字符串字面量分多行放置,编译器会将它们视为一个整体。例如,上述例子可如下重构:

通常情况下,只有当字符串字面量很长,在源代码中会跨越多行时,这样的结构才有利于提高可读性。生成的程序是相同的。

3.ASCII

美国信息交换标准代码(American Standard Code for Information Interchange,ASCII)表将整数与字符一一匹配。表2-4显示了ASCII表。对于十进制(0d)和十六进制(0x)的整数值,表中都给出了控制代码或可打印字符。

ASCII代码0~31是控制设备的控制代码字符。这些大多是不合时宜的。当美国标准协会在20世纪60年代正式确定ASCII时,当时的现代设备包括电传打字机、磁带阅读器和点阵打印机。目前仍在普遍使用的一些控制代码有:

❑ 0(NULL),被编程语言用作字符串的结束符。

❑ 4(EOT),EOT意指End Of Transmission,即传输结束,终止shell会话和与PostScript打印机的通信。

❑ 7(BELL),使设备发出声音。

❑ 8(BS),BS意指BackSpace,即退格,使设备擦除最后一个字符。

❑ 9(HT),HT意指Horizontal Tab,即水平制表符,将光标向右移动几个空格。

❑ 10(LF),LF意指Line Feed,即换行,在大多数操作系统中被用作行末标记。

❑ 13(CR),CR意指Carriage Return,即回车,在Windows系统中与LF结合使用,作为行末标记。

❑ 26(SUB),指替代字符(SUBstitute character)、文件结束和<Ctrl+Z>,在大多数操作系统上暂停当前执行的交互式进程。

ASCII表的其余部分(从32到127)是可打印字符。这些代表英文字符、数字和标点符号。

在大多数系统中,char类型的表示方法是ASCII。虽然它们之间的这种关系没有得到严格的保证,但它的确是一个标准。

表2-4 ASCII表

现在是时候将char类型、数组、for循环和ASCII表结合起来使用了。代码清单2-12显示了如何用字母构建数组,如何打印结果,以及如何将数组转换成大写字母的数组并再次打印。

代码清单2-12 使用ASCII打印小写和大写的英文字母

首先,我们声明一个长度为27的char数组来存放26个英文字母和一个null结尾符❶。接下来,采用for循环,使用迭代器变量i从0到25进行迭代。字母a的ASCII值为97。在迭代器变量i上添加97,我们可以生成小写字母表alphabet❷。要使alphabet成为以null结尾的字符串,需要将alphabet[26]设置为0❸。然后打印出结果❹。

接下来,我们来打印大写字母表。字母A的ASCII值是65,所以相应地重置了字母表的每个元素❺,并再次调用printf❻。