C++大学基础教程第 5章 数组
2009-7-29 -2-
5.1 数组基本概念
5.2 数组元素的下标
5.3 数组初始化
5.4 数组的大小和越界数组
5.5 字符数组
5.6 向函数传递数组
5.7 多维数组第 5章 数组
5.1 数组基本概念
2009-7-29 -4-
5.1数组基本概念数组是具有一定顺序关系的若干相同类型元素的集合。
组成数组的变量称为该数组的元素。
数组元素都有相同的变量名(数组名),但是有不同的下标。
2009-7-29 -5-
一维数组一维数组的说明方法为数据类型加数组名,再加方括号,里面含有元素个数。即:
类型说明符 数组名 [常量表达式 ];
数组名的命名规则和变量名相同。
常量表达式表示数组元素的个数,即数组长度。
2009-7-29 -6-
一个字符数组例子下面的代码说明一个字符数组:
char a [5];
即:字符数组,char a[5]”,存储该数组占 5个字节。每个元素的类型是字符型。数组下标从 0开始,分别是 a[0],a[1],a[2],a[3],a[4]。
请注意:数组元素的下标从 0开始。并且,a[5]
不属于该数组的空间范围。
2009-7-29 -7-
数组在内存中的存放数组元素都是存放在一片连续的内存单元中的。
下图显示 char name[]=”Ray Krebbs”的内存情况
2009-7-29 -8-
5.1.2数组的定义和简单变量相同,数组也必须先定义,然后才可以引用。定义数组的一般格式是:
类型说明符 数组名 [常量表达式 1] [常量表达式 2]…,
[常量表达式 n];
下面的语句定义了一个有五个整数的数组:
int ages[5];
数组 ages此时的内容是不确定的,编程中不能想当然地认为它全为 0。或其他什么值。
2009-7-29 -9-
定义数组应包括的几个方面
确定数组元素的类型
可以是如整型、浮点型等简单类型,也可以是结构体、
类等用户自定义类型。
确定数组的名称
由,标识符,指定,数组名是一个常量,代表着数组元素在内存中的起始地址。
确定数组的结构
包括数组的维数(有几个常量表达式),维的大小等,
也就是常量表达式的具体形式。常量表达式的值必须是正整数。
2009-7-29 -10-
错误的数组定义语句
void VoidArray[10];
//void不可以做数组类型
int a=9; float floatArray[a];
//下标表达式不可以是变量
char charArray[1.5+1.5];
//3.0不是整数
2009-7-29 -11-
例 5.1 定义一个含 10整数的数组解:以 ARRAY命名的数组,用 100至 109对数组元素赋值,
并对其求和。需要在一个循环中使用数组。这是使用数组最经常的方式。
void main()
{ int ARRAY [10];
int sum=0;
for (int i=0;i<10;i++)
{ ARRAY[i]=100+i;
sum+= ARRAY[i];
}
}
5.2 数组元素的下标
2009-7-29 -13-
5.2数组元素的下标数组的元素是由下标来区分的。使用数组时,只能分别对数组的各个元素操作。
对于一个已经声明过的数组,其元素的使用形式为:
数组名 [下标表达式 ]
例如
salaries [6];
int a=5;
salaries[a]=10;
2009-7-29 -14-
注意在使用数组元素时需要注意:
数组元素的下标表达式其结果必须为正整数。
数组元素的下标值不得超过声明时所定义的上下界。
2009-7-29 -15-
数组的下标数组元素的下标是数组元素到数组开始地址的偏移量。
第 1个元素的偏移量是 0个数组元素大小,第 2
个元素的偏移量是 1个数组元素大小,依次类推。
因此,数组元素是一系列大小相同的连续项,
每项到公共基点(数组起始地址)的偏移量是固定的。
5.3 数组初始化
2009-7-29 -17-
5.3数组初始化数组的初始化就是在定义数组时给部分或全部元素赋初值。
对于简单数据类型的数组,就是给数组元素赋值;
对于对象数组,每个元素都是某个类的一个对象,初始化就是调用该对象的构造函数。我们会在后面单独详细介绍。
2009-7-29 -18-
初始化数组的方法在使用数组元素之前,必须先对它们赋值。
有以下两种初始化数组的方法:
在定义数组时初始化
在程序中对数组进行初始化
2009-7-29 -19-
全局数组初始化
C++自动将全局数组变量中的所有元素初始化为
0或 null。
如果未赋初值,那么一个全局的字符数组中的所有元素将为 null(空 ),一个全局的数字数组中的所有元素将为 0。
程序中应尽量限制全局数组变量的使用。如果要用,也最好在程序中用语句将其显式初始化为 0,
以明确表示编程者的意图。
2009-7-29 -20-
5.3.1数组成员的初始化数组初始化时,用作初始化的数组元素值放在一个大括号 {}中例如:
int a[3]={ 1,1,l };
//含三个元素的整型数组,元素的值都是 1
2009-7-29 -21-
注意初始化值的个数可少于数组元素个数。没有被初始化的元素将赋值为 0。
初始化数组的值的个数不能多于数组元素个数,也不能使用逗号来省略对部分元素的初始化,这种做法在 C中是允许的,但在 C++中不允许。
2009-7-29 -22-
错误例子例如下面的代码对数组进行初始化是错误的:
int array1[5]={0,1,2,3,4,5};
//error初始化值个数多于数组元素个数
int array2[5]= {,,1,2,3 };
//error不能加入,,”来跳过不赋值的元素。
int array2[5]={0,,2,3,4};
//error不能加入,,”来跳过不赋值的元素。
int array3[5]={ };
//error语法格式错误
2009-7-29 -23-
例 5.2初始化全局和局部数组
#include <iostream>
using namespace std;
int array1[5]={1,2,3};
static int array2[5]={1};
void main()
{ int arr1[5]={2};
static int arr2[5]={1,2};
int n;
cout <<"global:\n";
for(n=0; n<5; n++)
cout <<" " <<array1[n];
运行结果为:
global,1 2 3 0 0 //全局数组初始化的结果
global static,1 0 0 0 0 //全局静态数组初始化的结果
local,2 0 0 0 0 //局部数组初始化的结果
local static,1 2 0 0 0 ////局部静态数组初始化的结果
cout <<"\nglobal static:\n";
for(n=0; n<5; n++)
cout <<" " <<array2[n];
cout <<"\nlocal:\n";
for(n=0; n<5; n++)
cout <<" " <<arr1[n];
cout <<"\nlocal static:\n";
for(n=0; n<5; n++)
cout <<" " <<arr2[n];
cout <<endl;
}
2009-7-29 -24-
5.3.2在程序中进行初始化大多数情况下,在定义数组时并不知道它的内容,需要在程序中,由用户输入或读磁盘文件数据对数组进行初始化。这时,for循环语句是一个强有力的工具。
在数组使用中,数组名不能出现在赋值语句中等号的左边。
2009-7-29 -25-
注意不能直接把一个数组赋给另一个数组。假设要将数组 total_sales的值拷贝到数组 saved_sales中,
使用下面的赋值方法就是错误的:
saved_sales =total_sales; //error
应使用一个循环语句将 total_sales中的元素的值逐个赋给数组 saved_sales中的每个元素。如下面的代码所示:
for (int i=0;i<ARRAY_SIZE;i++)
saved_sales[i] =total_sales[i];
2009-7-29 -26-
例 5.3 将 5个温度值赋给数组
#include<iostream.h>
void main()
{ float temp[5];
temp[0]=31.3;
temp[1]=28.7;
temp[2]=32.2;
temp[3]=34.5;
temp[4]=19.7;
cout<<"Daily temperature for 5 days\n";
for (int i=0;i<5;i++)
cout<<temp[i]<<"\n";
}
2009-7-29 -27-
5.3.3数组省略初始化方法有初始化的数组定义可以省略方括号中的数组大小。
例如,下面的代码中数组定义为 5个元素:
int array[]={0,1,2,3,4};
2009-7-29 -28-
数组的大小编译时必须知道数组的大小。通常,声明数组时方括号内的数字决定了数组的大小。
有初始化的数组定义又省略方括号中的数组大小时,编译器统计花括号之间的元素个数,以求出数组的大小。
例如,下面的代码产生相同的结果:
int array1[5]={0,1,2,3,4};
int array2[]={0,1,2,3,4};
2009-7-29 -29-
大括号的使用通过使用大括号,可初始化任何一种类型的数组。
例如,要记录前三年的销售总额,则可以如下定义并初始化一个数组:
double sales[]= {4323.43,122355.32,343324.96};
//sales= {4323.43,122355.32,343324.96};
注意:上面这种使用大括号来初始化数组的方法只能在定义数组时使用。在数组定义之后,就不能用这种方法了,而只能逐个元素地赋值。
2009-7-29 -30-
元素赋 0
如果在数组定义时,只对其中部分元素赋了初值,
C++会自动将其余的元素赋 0。
如果希望将一个大数组中的所有元素同时初始化为 0,则可以在定义这个数组时,只将数组的第一个元素赋初值 0。
这样,系统会自动把其余的元素都赋为 0。
5.4 数组的大小和越界数组
2009-7-29 -32-
5.4数组的大小和数组越界定义数组时,编译器必须知道数组的大小。
如果数组定义时省略了大小,只是由初始化的值来决定其大小。在程序中怎么知道数组的大小呢?
sizeof操作解决了该问题。
2009-7-29 -33-
sizeof
sizeof()能够返回传递给它的数据类型所占用内存的字节数。
如果将一个数组变量传给 sizeof(),则它将返回系统为此数组在内存中预留的字节数。
因此,数组的大小可用用以下公式来计算:
数组大小 = sizeof(数组名 ) / sizeof(数组类型 )
2009-7-29 -34-
例 5.7 用 sizeof确定数组的大小
#include <iostream>
using namespace std;
void main()
{ int a[]={1,2,4,8,16};
for(int i=0; i<(sizeof(a)/sizeof(int)); i++)
cout <<a[i] <<" ";
cout <<endl;
}
运行结果为:
1 2 4 8 16
2009-7-29 -35-
数组和字符串对于字符串的初始化,要注意数组实际分配的空间大小是字符串中字符个数加上末尾的 ’ \0’
结束符。
例 5.8 比较字符串数组大小和字符串长度的差别。
解:用 sizeof计算数组大小,用 strlen函数计算字符串长度。
2009-7-29 -36-
例 5.8中 exemptions和 sal_codes
两个数组在内存中的存放
5.5 字符数组
2009-7-29 -38-
5.5字符数组我们知道,字符串常量是用一对双引号括起来的字符序列,每个字符占一个字节,并在末尾添加 ’ \0’作为结尾标记。
2009-7-29 -39-
5.5.1字符数组定义
C++的基本数据类型变量中,却没有字符串变量,而是使用字符型数组来存放字符串。
如果我们对数组进行初始化赋值时,在末尾放置一个 ’ \0’,便构成了 C++字符串。
2009-7-29 -40-
例 5.9 字符数组的初始化和使用。
#include <iostream.h>
void main()
{ char str1[8]={112,114,111,103,114,97,109,0 };
char str2[8]={'P','r','o','g','r','a','m','\0' };
char str3[8]="program";
char str4[]="program";
for (int i=0;i<8;i++)
cout<<str1[i];
cout<<endl;
cout<<str2<<endl;
cout<<str3<<endl;
cout<<str4<<endl;
}
2009-7-29 -41-
注意字符串整体输入 /输出时,要注意:
◆ 输出字符不包括 ’ \0’。
◆ 输出字符串时,输出项是字符数组名,输出时遇到 ’ \0’结束。
◆ 输入多个字符串时,以空格分隔;输入单个字符串时其中不能有空格。
2009-7-29 -42-
5.5.2初始化字符数组初始化字符数组有两种方法,
一种是:
char array[10]={“hello”};
另一种是:
char array[10]={ ’h’,’e’,’l’,’l’,’o’,’\0’};
2009-7-29 -43-
比较第一种方法用途较广,初始化时,系统自动在数组没有填值的位置用 ’ \0’补上。另外,这种方法中的花括号可以省略,即能表示成:
char array[10]=“hello”;
第二种方法一次一个元素地初始化数组,如同初始化整型数组。这种方法通常用于输入不容易在键盘上生成的那些不可见字符。
例如,下面的代码中初始化值为若干制表符:
char array[10]={ ’\t’,’ \t’,’ \t’,’ \t’,’\0’};
2009-7-29 -44-
注意这里不要忘记为最后的 ’ \ 0’分配空间。如果要初始化一个字符串,hello”,那为它定义的数组至少有 6个数组元素。
例如,下面的代码给数组初始化,但会引起不可预料的错误:
char array[5]=“hello”;
该代码不会引起编译错误,但由于改写了数组空间以外的内存单元,所以是危险的。
2009-7-29 -45-
5.5.3 for循环用于字符数组字符数组,也称为字符串,实际上是 1字节的整数数组。
处理字符数组的方法与处理其他数组相同,但若考虑到了字符串末尾的 ’ \ 0’结束符,是很有益的。
2009-7-29 -46-
例 5.10字符串的输入和输出输入一个字符串,输出这个字符串及字符数目。
#include <iostream>
using namespace std;
void main()
{ int a=0;
char chArray[30];
cin>>chArray;
for(int i=0; chArray[i]!='\0'; i++)
{cout <<chArray[i];
a++;
}
cout <<endl;
cout<<"输入的字符数是,"<<a<<endl;
}
5.6 向函数传递数组
2009-7-29 -48-
5.6 向函数传递数组无论何时,将数组作为参数传给函数,实际上只是把数组的地址传给函数。
物理上,把整个数组放在栈中是不合理的,因为栈大小是一定且有限的。
如果把传送给函数的整个数组都放在栈中 (内存的大块复制 ),则很快会把栈空间用光。
2009-7-29 -49-
5.6.1传递给标准库函数
C++中有一个 memset()的函数,它可以一字节一字节地把整个数组设置为一个指定的值。
memset()函数在 memory.h头文件中声明,它把数组的起始地址作为其第一个参数,第二个参数是设置数组每个字节的值,第三个参数是数组的长度 (字节数,
不是元素个数 )。
其函数原型为:
void * memset(void *,int,unsigned);
其中 void*表示地址。
2009-7-29 -50-
例 5.11 传递数组名给 memset函数下面的代码用数组做参数传递给标准函数 memset(),以让其将数组设置成全 0。
void main()
{ int array1[5];
int array2[50];
memset(array1,0,5*sizeof(int));
memset(array2,0,50*sizeof(int));
}
memset()的第一个实参是数组名,数组名作参数即数组作参数,它仅仅只是一个数组的起始地址而已。
2009-7-29 -51-
5.6.2传递给自定义函数若要让一个函数求数组元素的和,需传递一个数组参数和数组大小参数。因为从传递的数组参数 (地址 )中,没有数组大小的信息。
2009-7-29 -52-
例 5.12 数组名作为函数的形参
using namespace std;
int sum(int [],int);
void main()
{ static int ia[5]={2,3,6,8,10};
int sumOfArray;
sumOfArray=sum(ia,5);
cout <<“数组元素的和等于,,
<<sumOfArray <<endl;
}
运行结果为;
数组元素的和等于,29
int sum(int array[],int len)
{ int iSum=0;
for(int i=0; i<len; i++)
iSum+=array[i];
return iSum;
}
2009-7-29 -53-
注意
sum()函数以整数数组作为第一个参数,以整数作为第二个参数。
由于传递数组名实际上传递的是地址,所以函数原型中,数组参数的书写形式无须在方括号中写明数组大小。
如果写明了数组大小,编译器将忽略之。数组形参的全方括号只是告诉函数,该参数是个数组的起始地址。
5.7 多维数组
2009-7-29 -55-
5.7.1理解多维数组一维数组是多个数值的单列表示,而多维数组则是数值的表格,甚至多表格表示,它具有多个下标值,最常用的表格是二维表格 (具有两个下标 )。
2009-7-29 -56-
例子假定要记录一个垒球队中每个队员的击球数。队中有 6个队员,
进行了 3场比赛。表中所示为击球记录。
队员姓名 1 1 1
张大明 2 2 2
李方春 3 3 3
林志松 2 2 2
崔明东 1 1 1
刘屈武 0 0 0
安度璧 1 1 1
2009-7-29 -57-
三维表的图示
C++提供存储多维数据的能力,
尽管现实世界很少碰到三维以上的情况。
2009-7-29 -58-
5.7.2多维数组的表示方式表示多维数组时,你必须让 C++明白数组具有多维。数组名后应具有多个下标,每个下标代表一个维、并用中括号括起。
例如,可使用下面的多维数组声明一个 6行 3列的整型数组 teams:
int teams[6][3]; //二维数组
2009-7-29 -59-
表 5.2球队数组的下标
team表具有 18个元素,元素下标值如下表
[0][0] [0][0] [0][0]
[0][1] [0][1] [0][1]
[0][2] [0][2] [0][2]
[1][0] [1][0] [1][0]
[1][1] [1][1] [1][1]
[1][2] [1][2] [1][2]
2009-7-29 -60-
总元素个数多维数组的总的元素个数可以通过下标相乘获得。
假如你要记录年度使用的账单,如下数组声明所示,
float utilities[12][4];
记录 12个月中四项开支的账单,数组元素为浮点类型。 12乘以 4得 48,于是这个数组有 48个元素。
2009-7-29 -61-
5.7.3数组在内存的映象
C++存储多维数组的方式与绝大多数其他的编程语言略有不同。
通过使用下标,不了解多维数组的内存映象也可以对其进行操作。
但是,大多数 C++程序员认为有必要深入理解数组在内存中的形式,这对编制高级应用程序很有用处。
2009-7-29 -62-
传递和接收多维数组二维数组实际上是,数组的数组,,它以行和列的形式出现,实际上还是一个一维数组,只不过数组的每个元素的类型不是整型,浮点型或字符型,而是另外一个数组。
传递和接收多维数组关键在于应明白多维数组是数组的数组。 C++通过地址参数传递数组,
多维数组也不例外。
2009-7-29 -63-
例子假定你定义了一个称为 scores的 5× 6的整型数组,把
scores传给一个称为 print_it()的函数:
print_it (scores); //pass table to a function
函数 print_it()必须知道所接收参数的类型以及该参数是一个数组,如果传递的参数是一维数组,可以如下编写语句:
print_it (int scores[]);
//Works only If scores is 1 dimensional

print_it (int scores[10]);
//Assuming scores have 10 elements
2009-7-29 -64-
C++按行存储多维数组通常,你无需过多考虑表格的物理存储方式,
多维表即是元素为数组的数组,编程时只要用下标表示它们的行、列位置就行了。
多维数组按行存储。考虑一个 4× 4的表格,图
5.4所示为这个表的直观映象,二维表内存映象依然是顺序的存储。
2009-7-29 -65-
二维表的内存映象
C++按行把多维数组映射到线性内存图示一个 4× 4的数组
int table[3][3]
的内存映象
2009-7-29 -66-
5.7.4定义多维数组
C++是宽松的,它允许在说明部分,初始化并定义多维数组。和一维数组一样,在初始化多维数组时要使用大括号。因为多维数组是数组的数组,所以在初始化时要使用嵌套的括号。
2009-7-29 -67-
例子初始化三个数组,aral,ara2和 ara3。
int aral[5]= {8,5,3,25,41};
//一维数组
int ara2[2][4]={{4,3,2,1},{1,2,3,4}};
//二维数组
int ara3[3][4]={{l,2,3,4},{5,6,7,8},{9,10,11,12}};
//二维数组
2009-7-29 -68-
以一维数组的形式进行初始化
C++亦允许多维数组以一维数组的形式进行初始化,
但必须注意要按行排列下面是 ara2和 ara3的另一种初始他形式:
int ara2[2][4]={4,3,2,1,1,2,3,4};
int ara3[3][4]={ l,2,3,4,5,6,7,8,9,10,11,12};
2009-7-29 -69-
二维数组看作是一维数组的一维数组一维数组的 4个元素是 ara_name[0]~
ara_name[3]。每一个元素则是其对应的一维数组的首地址。
2009-7-29 -70-
5.7.5表格与 for循环嵌套循环适用于多维数组注意到 for循环的循环次数与数组的下标数目相同,外层循环代表第一个下标 (行下标 );内层循环代表第二个下标 (列下标 )。
嵌套 for循环将遍历表中的每一个元素。
2009-7-29 -71-
例 5.13嵌套 for循环处理多维表
int main()
{for (row=0;row<2;row++)
{for (col=0;col<4;col++)
cout<<row<<”,<<col<<"\n";
}
}
将产生如下输出结果
0 0
0 1
0 2
1 0
1 1
1 2
2009-7-29 -72-
注意表格的数据输入可以使用 cin(),get()或其他函数,
也可以在说明表格时为元素赋值。
通常使用来自磁盘上的数据文件。不管使用什么方法存储多维数组的数值,都可以使用嵌套 for
循环遍历每个元素
2009-7-29 -73-
例 5.15 输出带标题的二维表格
void main()
{ float disk[2][4]; //存放磁盘价格表
int row,col;
disk[0][0]=2.30; //第一行第一列
disk[0][1]=2.75;
disk[0][2]=3.20;
disk[0][3]=3.50;
disk[1][0]=1.75;
disk[1][1]=2.10;
disk[1][2]=2.60;
disk[1][3]=2.95;
2009-7-29 -74-
cout<<"\tSingle-Side,\tDouble-Side"
<<"\tSingle-Side,\tDouble-Side\n";
cout<<"\tDouble-density,\tDouble-density"
<<"\tHigh-density,\tHigh-density\n";
for (row=0;row<2;row++) //打印表格
{ if (row==0)
cout<<"3 inch\t";
else
cout<<"5 inch\t";
for (col=0;col<4;col++)
cout<<"$"<<setprecision(2)
<<disk[row][col]<<"\t\t";
cout<<"\n";
}
}
2009-7-29 -75-
程序运行结果输出带标题的二维表格
Single-Side
Double-density,
Double-Side
Double-
density
Single-Side
High-density
Double-Side
High-density
3 inch $2.30 $2.75 $3.20 $3.50
5 inch $1.75 $2.10 $2.60 $2.95
2009-7-29 -76-
本章小结本章介绍了如何定义和初始化一个数组。数组的初始化可以在声明部分进行,也可以在程序体中进行。
现在我们已经学习过了如何定义,初始化及处理多维数组,
尽管并非所有数据适用于表格的紧缩格式,但它确实在很多时候非常有用。使用嵌套 for循环可以遍历多维数组。
通过本章的学习,我们知道,通过引入数组,处理数组元素比处理相同个数的单个变量要简单得多