- 👋 Hi, I’m liubo
- 👀 I’m interested in harmony
- 🌱 I’m currently learning harmony
- 💞️ I’m looking to collaborate on …
- 📫 How to reach me …
数组
数组(array)是一种数据格式,能够存储多个同类型的值。例如,数组可以存储60个int类型的值、12个short值或365个float值。每个值都存储在一个独立的数组元素中,计算机在内存中依次存储数组的各个元素。
在C++中,数组名被认为是数组在内存中存放的首地址。
要创建数组,可使用声明语句。数组声明应指出以下三点:
-
存储在每个元素中的值的类型;
-
数组名;
-
数组中的元素数。
数组格式
声明数组的通用格式如下:
typeName arrayName[arraySize];
表达式arraySize指定元素数目,它必须是整型常数(如10)或const值,也可以是常量表达式(如8 * sizeof(int)),即其中所有的值在编译时都是已知的。具体地说,arraySize不能是变量,变量的值是在程序运行时设置的。然而,本章稍后将介绍如何使用new运算符来避开这种限制。
在C++中,可以通过修改简单变量的声明,添加中括号(其中包含元素数目)来完成数组声明。
例如,下面的声明创建一个名为 months 的数组,该数组有12个元素,每个元素都可以存储一个short类型的值:
short months[12]; // creats array of 12 short
事实上,可以将数组中的每个元素看作是一个简单变量。
作为复合类型的数组
数组之所以被称为复合类型,是因为它是使用其他类型来创建的(C语言使用术语“派生类型”,但由于C++对类关系使用术语“派生”,所以它必须创建一个新术语)。不能仅仅将某种东西声明为数组,它必须是特定类型的数组。没有通用的数组类型,但存在很多特定的数组类型,如char数组或long数组。例如,请看下面的声明:
float loans[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
/***********************************************************
* @file name: arrayone.cpp
* @author: liubo
* @date: 2024.01.05
* @describe: small arrays of integers
*
**********************************************************/
#include<iostream>
using std::cin;
using std::cout;
using std::endl;
int main()
{
cout << "----------------begain------------------" << endl;
int yams[3]; // creates array with three elements
yams[0] = 7; // assign value to first element
yams[1] = 8;
yams[2] = 8;
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 = " << yams[0] + yams[1] + yams[2] << endl;
cout << "The package with " << yams[1] << " yams coat ";
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 tatol yam expense is " << total << " cents.\n";
cout << "\nSize of yams array = " << sizeof yams << " bytes.\n";
cout << "Size of one element = " << sizeof yams[0] << " byte.\n";
cout << "----------------end------------------" << endl;
return EXIT_SUCCESS;
}
/**
* @expected output:
----------------begain------------------
Total yams = 23
The package with 8 yams coat 30 cents per yam.
The tatol yam expense is 420 cents.
Size of yams array = 12 bytes.
Size of one element = 4 byte.
----------------end------------------
*/
程序说明:
该程序首先创建一个名为yams的包含3个元素的数组。由于yams有3个元素,它们的编号为0~2,因此arrayone.cpp使用索引0~2分别给这三个元素赋值。yam的每个元素都是int,都有int类型的权力和特权,因此arrayone.cpp能够将值赋给元素、将元素相加和相乘,并显示它们。
程序给yam的元素赋值时,绕了一个大弯。C++允许在声明语句中初始化数组元素。程序清单4.1使用这种捷径来给yamcosts数组赋值:
int yamcosts[3] = {20, 30, 5};
只需提供一个用逗号分隔的值列表(初始化列表),并将它们用花括号括起即可。列表中的空格是可选的。如果没有初始化函数中定义的数组,则其元素值将是不确定的,这意味着元素的值为以前驻留在该内存单元中的值。
接下来,程序使用数组值进行一些计算。程序的这部分由于包含了下标和括号,所以看上去有些混乱。for循环可以提供一种功能强大的方法来处理数组,因而不用显式地书写每个索引。同时,我们仍然坚持使用小型数组。
您可能还记得,sizeof运算符返回类型或数据对象的长度(单位为字节)。注意,如果将sizeof运算符用于数组名,得到的将是整个数组中的字节数。但如果将sizeof用于数组元素,则得到的将是元素的长度(单位为字节)。这表明yams是一个数组,而yams[1]只是一个int变量。
数组的初始化规则
C++有几条关于初始化数组的规则,它们限制了初始化的时刻,决定了数组的元素数目与初始化器中值的数目不相同时将发生的情况。我们来看看这些规则。
只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组:
int cards[4] = {3, 6, 8, 10}; // okey
int hand[4]; // okey
hand[4] = {5, 6, 7, 9}; // not allowed
hand = cards; // not allowed
然而,可以使用下标分别给数组中的元素赋值。
初始化数组时,提供的值可以少于数组的元素数目。例如,下面的语句只初始化hotelTips的前两个元素:
float hotelTips[5] = {5.0, 2.5};
如果只对数组的一部分进行初始化,则编译器将把其他元素设置为0。因此,将数组中所有的元素都初始化为0非常简单——只要显式地将第一个元素初始化为0,然后让编译器将其他元素都初始化为0即可:
long totals[500] = {0};
如果初始化为{1}而不是{0},则第一个元素被设置为1,其他元素都被设置为0。
如果初始化数组时方括号内([ ])为空,C++编译器将计算元素个数。例如,对于下面的声明:
short thing[] = {3, 5, 3, 8};
编译器将使things数组包含4个元素。
让编译器去做
通常,让编译器计算元素个数是种很糟的做法,因为其计数可能与您想象的不一样。例如,您可能不小心在列表中遗漏了一个值。然而,这种方法对于将字符数组初始化为一个字符串来说比较安全,很快您将明白这一点。如果主要关心的问题是程序,而不是自己是否知道数组的大小,则可以这样做:
short thing[] = {1, 5, 3, 8};
int num_elements = sizeof things / sizeof (short);
这样做是有用还是偷懒取决于具体情况。
C++11数组初始化方法
C++11使用大括号的初始化(列表初始化)作为一种通用初始化方式,可用于所有类型。数组以前就可使用列表初始化,但C++11中的列表初始化新增了一些功能。
首先,初始化数组时,可省略等号(=):
double earning[4] {1.2e4, 1.6e4, 1.1e4, 1.7e4}; // okey with C++11
其次,可不在大括号内包含任何东西,这将把所有元素都设置为零:
unsigned int counts[10] = {}; // all elements set to 0
float balances[100] {}; // all elements set to 0
第三,列表初始化禁止缩窄转换:
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章将更详细地讨论它们。
数组名作函数参数
用数组名作函数参数,实参与形参都应用数组名。
这时,函数传递的是数组在内存中的地址。实参中的数组地址传到形参中,实参形参共用同一段内存。
1、用数组名作函数参数,应在主调函数和被调函数中分别定义数组,且类型一致。
2、需指定实参数组大小,形参数组的大小可不指定。数组名作实参实际上是传递数组的首地址。
void fun(int a[2]) {
for(int i=0;i<2;i++) {
a[i]=a[i]*a[i];
}
}
void main(void) {
int b[2]={2,4};
cout<<b[0]<<‘\t’<<b[1]<<endl;
fun(b);
cout<<b[0]<<‘\t’<<b[1]<<endl;
}
输出: 2 4
4 16
void sort(int x[ ], int n) {
int t,i,j;
for( i=0;i<n-1;i++) {
for(j=0;j<n-i-1;j++) {
if(x[j]>x[j+1]) {
t=x[j]; x[j]=x[j+1]; x[j+1]=t;
}
}
}
}
void main(void) {
int a[5]={20,4,16,8,10};
sort(a, 5 );
for(int i=0;i<5;i++)
cout<<a[i]<<'\t';
}