C++PrimerPlus 第四章 复合类型 - 4.1数组

4.1 数组

数组(array)是一种数据格式,能够存储多个同类型的值。例如,数组可以存储60个int类型的值(这些值表示游戏5年来的销售量)、12个short值(这些值表示每个月的天数)或365个float值(这些值指出一年中每天在食物方面的开销)。每个值都存储在一个独立的数组元素中,计算机在内存中依次存储数组的各个元素。

要创建数组,可使用声明语句。数组声明应指出以下三点:

  • 存储在每个元素中的值的类型;
  • 数组名;
  • 数组中的元素数。

在C++中,可以通过修改简单变量的声明,添加中括号(其中包含元素数目)来完成数组声明。例如,下面的声明创建一个名为months的数组,该数组有12个元素,每个元素都可以存储一个short类型的值:

short months[12];	//creates array of 12 short

事实上,可以将数组中的每个元素看作是一个简单变量。

声明数组的通用格式如下:
typeName arrayName[arraySize];

表达式arraySize指定元素数目,它必须是整型常数(如10)或const值,也可以是常量表达式(如8*sizeof(int)),即其中所有的值在编译时都是已知的。具体地说,arraySize不能是变量,变量的值是在程序运行时设置的。然而,本章稍后将介绍如何使用new运算符来避开这种限制。

作为复合类型的数组

数组之所以被称为复合类型,是因为它是使用其他类型来创建的(C语言使用术语“派生类型”,但由于C++对类关系使用术语“派生”,所以它必须创建一个新术语)。不能仅仅将某种东西声明为数组,它必须是特定类型的数组。没有通用的数组类型,但存在很多特定的数组类型,如char数组或long数组。例如,请看下面的声明:

float locans[20];

loans的类型不是“数组”,而是“float数组”。这强调了loans数组是使用float类型创建的。

数组的很多用途都是基于这样一个事实:可以单独访问数组元素。方法是使用下标或索引来对元素进行编号。C++数组从0开始编号(这没有商量的余地,必须从0开始。Pascal和BASIC用户必须调整习惯)。C++使用带索引的方括号表示法来指定数组元素。例如,months[0]是months数组的第一个元素,months[11]是最后一个元素。注意,最后一个元素的索引比数组长度小1(参见下图)。因此,数组声明能够使用一个声明创建大量的变量,然后便可以用索引来标识和访问各个元素。
在这里插入图片描述

有效下标值的重要性

编译器不会检查使用的下标是否有效。例如,如果将一个值赋给不存在的元素months[101],编译器并不会指出错误。但是程序运行后,这种赋值可能引发问题,它可能破坏数据或代码,也可能导致程序异常终止。所以必须确保程序只使用有效的下标值。

程序清单4.1中的马铃薯分析程序说明了数组的一些属性,包括声明数组、给数组元素赋值以及初始化数组。

程序清单4.1 arrayone.cpp

//array.cpp -- small arrays of integers
#include<iostream>
int main()
{
	using namespace std;
	int yams[3];	//creates array with three elements
	yams[0] = 7;	//assign value to first element
	yams[1] = 8;
	yams[2] = 6;

	int yamcosts[3] = { 20,30,5 };	//create, initialize array
	//Note: If your C++ compiler or translator can't initialize
	//this array, use static int yamcosts[3] instead of
	//int yamcosts[3]

	cout << "Total yams = ";
	cout << yams[0] + yams[1] + yams[2] << endl;
	cout << "The package with " << yams[1] << " yams costs ";
	cout << yamcosts[1] << " cents per yam.\n";
	int total = yams[0] * yamcosts[0] + yams[1] * yamcosts[1];
	total = total + yams[2] * yamcosts[2];
	cout << "The total yam expense is " << total << " cents.\n";

	cout << "\nSize of yams array = " << sizeof yams;
	cout << " bytes.\n";
	cout << "Size of one element = " << sizeof yams[0];
	cout << " bytes.\n";
	return 0;
}

下面是该程序的输出:
Total yams = 21
The package with 8 yams costs 30 cents per yam.
The total yam expense is 410 cents.

Size of yams array = 12 bytes.
Size of one element = 4 bytes.

4.1.1 程序说明

该程序首先创建一个名为yams的包含3个元素的数组。由于yams有3个元素,它们的编号为02,因此arrayone.cpp使用索引02分别给这三个元素赋值。yam的每个元素都是int,都有int类型的权力和特权,因此arrayone.cpp能够将值赋给元素、将元素相加和相乘,并显示它们。

程序给yam的元素赋值时,饶了一个大弯。C++允许在声明语句中初始化数组元素。程序清单4.1使用这种捷径来给yamcosts数组赋值:

int yamcosts[3] = {20, 30, 5};

只需提供一个用逗号分隔的值列表(初始化列表),并将它们用花括号括起来即可。列表中的空格是可选的。如果没有初始化函数中定义的数组,则其元素值将是不确定的,这意味着元素的值为以前驻留在该内存单元中的值。

接下来,程序使用数组值进行一些计算。程序的这部分由于包含了下标和括号,所以看上去有些混乱。第5章将介绍for循环,它可以提供一种功能强大的方法来处理数组,因而不用显式地书写每个索引。同时,我们仍然坚持使用小型数组。

您可能还记得,sizeof运算符返回类型或数据对象的长度(单位为字节)。注意,如果将sizeof运算符用于数组名,得到的将是整个数组中的字节数。但如果将sizeof用于数组元素,则得到的将是元素的长度(单位为字节)。这表明yams是一个数组,而yams[1]只是一个int变量。

4.1.2 数组的初始化规则

C++有几条关于初始化数组的规则,它们限制了初始化的时刻,决定了数组的元素数目与初始化器中值的数目不相同时将发生的情况,我们来看看这些规则。

只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组:

int cards[4] = {3, 6, 8, 10};	//okay
int hand[4];					//okay
hand[4] = {5, 6, 7, 9};			//not allowed
hand = cards;					//not allowed

然而,可以使用下标分别给数组中的元素赋值。

初始化数组时,提供的值可以少于数组的元素数目。例如,下面的语句只初始化hotalTips的前两个元素:

float hotalTips[5] = {5.0, 2.5};

如果只对数组的一部分进行初始化,则编译器将把其他元素设置为0。因此,将数组中所有的元素都初始化为0非常简单——只要显式地将第一个元素初始化为0,然后让编译器将其他元素都初始化为0即可:

long totals[500] = {0};

如果初始化为{1}而不是{0},则第一个元素被设置为1,其他元素都被设置为0。

如果初始化数组时方括号内([])为空,C++编译器将计算元素个数。例如,对于下面的声明:

short things[] = {1, 5, 3, 8};

编译器将使things数组包含4个元素。

让编译器去做

通常,让编译器计算元素个数是种很糟的做法,因为其计算可能与您想象的不一样。例如,您可能不小心在列表中遗漏了一个值。然而,这种方法对于将字符数组初始化为一个字符串来说比较安全,很快您将明白这一点。如果主要关心的问题是程序,而不是自己是否知道数组的大小,则可以这样做:

short things[] = {1, 5, 3, 8};
int num_elements = sizeof things / sizeof(short); 

这样做是有用还是偷懒取决于具体情况。

4.1.3 C++11数组初始化方法

第3章说过,C++11将使用大括号的初始化(列表初始化)作为一种通用初始化方式,可用于所有类型。数组以前就可使用列表初始化,但C++11中的列表初始化新增了一些功能。

首先,初始化数组时,可省略等号(=):

double earnings[4] {1.2e4, 1.6e4, 1.1e4, 1.7e4};		//okay with C++11

其次,可不在大括号内包含任何东西,这将把所有元素都设置为零:

unsigned int counts[10] = {};		//all elements set to 0
float balances[100]{};				//all elements set to 0

第三,列表初始化禁止缩窄转换,这在第3章介绍过:

long plifs[] = {25, 92, 3.0};				//not allowed
char slifs[4] {‘h’, ‘i’, 1122011, ‘\0};	//not allowed
char tlifs[4] {‘h’, ‘i’, 112, ‘\0};		//allowed

在上述代码中,第一条语句不能通过编译,因为将浮点数转换为整型是缩窄操作,即使浮点数的小数点后面为零。第二条语句也不能通过编译,因为1122011超出了char变量的取值范围(这里假设char变量的长度为8位)。第三条语句可通过编译,因为虽然112是一个int值,但它在char变量的取值范围内。

C++标准模板库(STL)提供了一种数组替代品——模板类vector,而C++11新增了模板类array。这些替代品比内置复合类型数组更复杂、更灵活,本章将简要地讨论它们,而第16章将更详细地讨论它们。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hank_W

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值