- 内容提要
- 前言
- 第 1 章 预备知识
- 第 2 章 开始学习 C++
- 第 3 章 处理数据
- 第 4 章 复合类型
- 第 5 章 循环和关系表达式
- 第 6 章 分支语句和逻辑运算符
- 第 7 章 函数——C++的编程模块
- 第 8 章 函数探幽
- 第 9 章 内存模型和名称空间
- 第 10 章 对象和类
- 第 11 章 使用类
- 第 12 章 类和动态内存分配
- 第 13 章 类继承
- 第 14 章 C++中的代码重用
- 第 15 章 友元、异常和其他
- 第 16 章 string 类和标准模板库
- 第 17 章 输入、输出和文件
- 第 18 章 探讨 C++新标准
- 附录 A 计数系统
- 附录 B C++保留字
- 附录 C ASCII 字符集
- 附录 D 运算符优先级
- 附录 E 其他运算符
- 附录 F 模板类 string
- 附录 G 标准模板库方法和函数
- 附录 H 精选读物和网上资源
- 附录 I 转换为 ISO 标准 C++
- 附录 J 复习题答案
4.1 数组
数组(array)是一种数据格式,能够存储多个同类型的值。例如,数组可以存储 60 个 int 类型的值(这些值表示游戏 5 年来的销售量)、12 个 short 值(这些值表示每个月的天数)或 365 个 float 值(这些值指出一年中每天在食物方面的开销)。每个值都存储在一个独立的数组元素中,计算机在内存中依次存储数组的各个元素。
要创建数组,可使用声明语句。数组声明应指出以下三点:
- 存储在每个元素中的值的类型;
- 数组名;
- 数组中的元素数。
在 C++中,可以通过修改简单变量的声明,添加中括号(其中包含元素数目)来完成数组声明。例如,下面的声明创建一个名为 months 的数组,该数组有 12 个元素,每个元素都可以存储一个 short 类型的值:
事实上,可以将数组中的每个元素看作是一个简单变量。
声明数组的通用格式如下:
表达式 arraySize 指定元素数目,它必须是整型常数(如 10)或 const 值,也可以是常量表达式(如 8 * sizeof(int)),即其中所有的值在编译时都是已知的。具体地说,arraySize 不能是变量,变量的值是在程序运行时设置的。然而,本章稍后将介绍如何使用 new 运算符来避开这种限制。
作为复合类型的数组
数组之所以被称为复合类型,是因为它是使用其他类型来创建的(C 语言使用术语“派生类型”,但由于 C++对类关系使用术语“派生”,所以它必须创建一个新术语)。不能仅仅将某种东西声明为数组,它必须是特定类型的数组。没有通用的数组类型,但存在很多特定的数组类型,如 char 数组或 long 数组。例如,请看下面的声明:
loans 的类型不是“数组”,而是“float 数组”。这强调了 loans 数组是使用 float 类型创建的。
数组的很多用途都是基于这样一个事实:可以单独访问数组元素。方法是使用下标或索引来对元素进行编号。C++数组从 0 开始编号(这没有商量的余地,必须从 0 开始。Pascal 和 BASIC 用户必须调整习惯)。C++使用带索引的方括号表示法来指定数组元素。例如,months[0]是 months 数组的第一个元素,months[11]是最后一个元素。注意,最后一个元素的索引比数组长度小 1(参见图 4.1)。因此,数组声明能够使用一个声明创建大量的变量,然后便可以用索引来标识和访问各个元素。
图 4.1 创建数组
有效下标值的重要性
编译器不会检查使用的下标是否有效。例如,如果将一个值赋给不存在的元素 months[101],编译器并不会指出错误。但是程序运行后,这种赋值可能引发问题,它可能破坏数据或代码,也可能导致程序异常终止。所以必须确保程序只使用有效的下标值。
程序清单 4.1 中的马铃薯分析程序说明了数组的一些属性,包括声明数组、给数组元素赋值以及初始化数组。
程序清单 4.1 arrayone.cpp
下面是该程序的输出:
4.1.1 程序说明
该程序首先创建一个名为 yams 的包含 3 个元素的数组。由于 yams 有 3 个元素,它们的编号为 0~2,因此 arrayone.cpp 使用索引 0~2 分别给这三个元素赋值。Yam 的每个元素都是 int,都有 int 类型的权力和特权,因此 arrayone.cpp 能够将值赋给元素、将元素相加和相乘,并显示它们。
程序给 yam 的元素赋值时,绕了一个大弯。C++允许在声明语句中初始化数组元素。程序清单 4.1 使用这种捷径来给 yamcosts 数组赋值:
只需提供一个用逗号分隔的值列表(初始化列表),并将它们用花括号括起即可。列表中的空格是可选的。如果没有初始化函数中定义的数组,则其元素值将是不确定的,这意味着元素的值为以前驻留在该内存单元中的值。
接下来,程序使用数组值进行一些计算。程序的这部分由于包含了下标和括号,所以看上去有些混乱。第 5 章将介绍 for 循环,它可以提供一种功能强大的方法来处理数组,因而不用显式地书写每个索引。同时,我们仍然坚持使用小型数组。
您可能还记得,sizeof 运算符返回类型或数据对象的长度(单位为字节)。注意,如果将 sizeof 运算符用于数组名,得到的将是整个数组中的字节数。但如果将 sizeof 用于数组元素,则得到的将是元素的长度(单位为字节)。这表明 yams 是一个数组,而 yams[1]只是一个 int 变量。
4.1.2 数组的初始化规则
C++有几条关于初始化数组的规则,它们限制了初始化的时刻,决定了数组的元素数目与初始化器中值的数目不相同时将发生的情况。我们来看看这些规则。
只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组:
然而,可以使用下标分别给数组中的元素赋值。
初始化数组时,提供的值可以少于数组的元素数目。例如,下面的语句只初始化 hotelTips 的前两个元素:
如果只对数组的一部分进行初始化,则编译器将把其他元素设置为 0。因此,将数组中所有的元素都初始化为 0 非常简单—只要显式地将第一个元素初始化为 0,然后让编译器将其他元素都初始化为 0 即可:
如果初始化为{1}而不是{0},则第一个元素被设置为 1,其他元素都被设置为 0。
如果初始化数组时方括号内([ ])为空,C++编译器将计算元素个数。例如,对于下面的声明:
编译器将使 things 数组包含 4 个元素。
让编译器去做
通常,让编译器计算元素个数是种很糟的做法,因为其计数可能与您想象的不一样。例如,您可能不小心在列表中遗漏了一个值。然而,这种方法对于将字符数组初始化为一个字符串来说比较安全,很快您将明白这一点。如果主要关心的问题是程序,而不是自己是否知道数组的大小,则可以这样做:
这样做是有用还是偷懒取决于具体情况。
4.1.3 C++11 数组初始化方法
第 3 章说过,C++11 将使用大括号的初始化(列表初始化)作为一种通用初始化方式,可用于所有类型。数组以前就可使用列表初始化,但 C++11 中的列表初始化新增了一些功能。
首先,初始化数组时,可省略等号(=):
其次,可不在大括号内包含任何东西,这将把所有元素都设置为零:
第三,列表初始化禁止缩窄转换,这在第 3 章介绍过:
在上述代码中,第一条语句不能通过编译,因为将浮点数转换为整型是缩窄操作,即使浮点数的小数点后面为零。第二条语句也不能通过编译,因为 1122011 超出了 char 变量的取值范围(这里假设 char 变量的长度为 8 位)。第三条语句可通过编译,因为虽然 112 是一个 int 值,但它在 char 变量的取值范围内。
C++标准模板库(STL)提供了一种数组替代品—模板类 vector,而 C++11 新增了模板类 array。这些替代品比内置复合类型数组更复杂、更灵活,本章将简要地讨论它们,而第 16 章将更详细地讨论它们。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论