nbhkdz.com冰点文库

第五讲 数组

时间:2016-03-03


第五讲 数组
一、数组的概念和定义
到现在为止,我们已经可以编写一些简单程序了,大家不 能由此就这样来认识,认为在程序设计时,变量之间都是独立 的,一个变量只能表示一个数据。其实,有些时候,为了便于 描述、处理,我们经常用一个变量来表示一批数据,而这类变 量中,最常用的是数组。 数组(array)是一种常用的数据类型,一个数组就是线 性存储的一系列相同类型的值

,例如10个字符或200个整数。 整个数组有一个单一的名字,数组中的元素可以使用一个整数 下标(或称为索引)来进行访问。注意,数组元素的下标的编 号是从0而不是1开始的。

一、数组的概念和定义
从下面的例子中,可以看出引入数组的作用。 输入n(n<100)个数,并且以相反的顺序输出这些数。 当n值相对较小时,如n=5,我们可以通过定义5个不同的变量很 轻松的解决这个问题。程序为(假设五个变量分别为x1、x2、x3、 x4、x5)
int x1,x2,x3,x4,x5;
scanf(“%d%d%d%d%d”,&x1,&x2,&x3,&x4,&x5) ; printf(“%d %d %d %d %d\n”,x5,x4,x3,x2,x1);

但是当n值超过一定个数时,如n=100,定义变量及数 据的输入、输出成为一个繁琐的过程。

一、数组的概念和定义
利用数组就能很方便的处理这个问题,且程序规模不受n的影响。 假设n=100,我们引入数组x,分别用x[1],x[2],…,x[100] 表示这100个变量,其中x为数组名称,方括号中的数字成为下标,下标可 以用变量表示,如x[i]表示的数据由i决定,当i=1,x[i]表示x[1], i=20,x[i]表示x[20],i=100,x[i]表示x[100],……,不同的 x[i]就是不同的数组元素,于是程序可以修改为:
int x[100],i; for(i=0;i<=99;i++) scanf(“%d”,&x[i]);

for(i=99;i>=0;i--)
printf(“%d ”,x[i]);

一、数组的概念和定义
可见,引入数组之后,程序设计变得十分有效。特别是对于一批 数据的排序、查找等操作、不使用数组将寸步难行。 我们可以使用声明来告诉编译器在程序中需要一个数组。数组声 明中包括数组元素的数目和元素的类型。编译器根据这些信息创建合 适的数组。下面是一些数组声明的例子: int main(void) { int a[50]; char code[26]; float x[365]; … }

注意,下标必须是整数且从0开始。数组 中的元素在内存中是顺序存储的,如下图所示。
int a[5] 1982 a[0] 65 a[1] 9037 a[2] 122 a[3] 76 a[4]

二、数组初始化
对于单个数值变量(有时也称标量),可以在声明中用表达式来初始 化它,例如:

int a=1;
char ch=?a?; C为数组的初始化引入了以下新语法:

int day[12]={31,28,31,30,31,30,31,31,30,31,30,31};
从上例中可以看出,可以使用花括号括起来的一系列数值来初始化数 组。数值之间用逗号隔开。这样首元素(day[0])赋值为31,依次类推。

使用未经初始化的数组会出现什么情况?
与普通变量相似,在初始化之前数组元素的值是不确定的。编译器使 用的数值是存储单元中已有的数值。

二、数组初始化
初始化列表中的元素数目应该和数组大小一致,如果二者不一致会出 现什么情况?例如:

int days[12]={31,28,31,30,31,30,31,31,30,31,};
从上面结果我们可以知道,当数值数目少于数组元素数目时,多余的 数组元素被初始化为0。也就是说,如果不初始化数组,数组元素和未初 始化的普通变量一样,期中存储的是无用的数值;但是如果部分初始化数 组,未初始化的元素则被赋值为0。 如果出事化列表中项目的个数大于数组大小编译器则会毫不留情地认 为这是一个错误。

三、为数组赋值
声明完数组后,也可以使用循环对数组元素逐个赋值(或读入 数据)。例如: int days[12],i;

for(i=0;i<=11;i++)
scanf(“%d”,&days[i]); C不支持把数组作为一个整体来进行赋值,也不支持花括号括起 来的列表形式进行赋值(初始化的时候例外)。以下的这些赋值方 式均不允许出现: int a[5]={5,3,2,8,6}; int b[5]; //初始化,允许

b=a;
b={5,3,2,8,6};

//不允许
//不允许

四、数组边界
使用数组的时候,需要注意数组下标不能超过数组的边界,也 就是说,数组下标应该具有对于数组来说有效的值。例如,有如下 数组的声明: int days[12]; 那么在使用数组下标的时候,要确保它的范围在0到11之间,因 为编译器不会为你检查出这种错误。 也许有人会问,为什么C会允许这种事情发生。这仍然是处于C 信任程序员的原则。不检查边界能够让C程序的运行速度更快。

四、数组边界
避免出现这个问题比较简单的方法是:在数组声明中使用符号常 量,然后程序中需要使用数组大小的地方都直接引用符号常量:
#define SIZE 12

int main(void)
{ int days[SIZE],i;

for(i=0;i<SIZE;i++)
… }

五、多维数组
例如:气象分析员要分析5年中每月的降水量数据,首先要解决 的问题是如何表示这些数据。一种方法是使用一个60个元素的数组, 但是如果把各年度的数据单独放置会跟好。也可以设置5个数组,每 个数组包含12个元素。这种办法还是比较笨拙,而且如果要处理的数 据不是5年,而是50年,这种方法就很不合适。 更好的处理方法是使用一个数组的数组,即:主数组包含5个元 素,每个元素代表一年。代表一年的元素是包含12个元素的数组。这 种数组的数组我们称之为二维数组。下面是这种数组的声明方法:
float rain[5][12] float rain[5][12] //粗体部分说明rain是一个包含5个元素的数组。 //粗体部分说明每个元素的类型是float[12]。

五、多维数组
按此推理,rain的首元素rain[0]是一个包含12个float数值的数 组。rain[1],rain[2]等等也是如此。rain[0]是数组,那么它的首元 素是rain[0][0],第二个元素是rain[0][1],依次类推。那么如果要访 问位于2行3列的元素,则应该用rain[2][3](注意:数组中计数是从 0开始的,因此2行实际指的是第3行,3列实际指的第4列)
用二维视图表示数组便于我们直观地想象具有两个下标的数组。如下图 所示。改变第二个下标,可以沿着一行一动,每移动一个单位代表一个月份。 改变第一个下标,可以沿着一列垂直移动,每移动一个单位代表一年。 12个月 rain[0][0] rain[1][0] rain[2][0] rain[3][0] … 5 年 rain[0][1] rain[1][1] rain[2][1] rain[3][1] … rain[0][2] rain[1][2] rain[2][2] rain[3][2] … rain[0][3] rain[1]3] rain[2][3] rain[3][3] … … … … …

五、多维数组
实际上,二维数组也是顺序存储的,前12个元素之后,跟着就是 第二个包含12个元素的数组,依次类推。

六、初始化多维数组
对二维数组的初始化是建立在对一维数组的初始化之上的,因此可 以采用以逗号分隔的5个数值列表来初始化像rain这样的二维数组:
const float rain[YEARS][MONTHS]={ {4.3,4.3,4.3,3.0,2.0,1.2,0.2,0.2,0.4,2.4,3.5,6.6}, {8.5,8.2,1.2,1.6,2.4,0.0,5.2,0.9,0.3,0.9,1.4,7.3}, {9.1,8.5,6.7,4.3,2.1,0.8,0.2,0.2,1.1,2.3,6.1,8.4}, {7.2,9.9,8.4,3.3,1.2,0.8,0.4,0.0,0.6,1.7,4.3,6.2}, {7.6,5.6,3.8,2.8,3.8,0.2,0.0,0.0,0.0,1.3,2.6,5.2} };

六、初始化多维数组
这个初始化使用了5个数值列表,每个数值列表都用花括号括起来。 第一个列表被赋值给第一行,第二个列表被赋值给数组的第二行,依次 类推。前面在一维数组中讨论的数据个数和数组大小不匹配问题同样适 用于此处的每一行。也就是说,如果第一个列表中有10个数值,则第 一行只有前10个元素得到赋值,最后2个元素被默认初始化为0.如果列 表中的数值多于12个,则报告错误;而且这些数值不会影响到下一行 的赋值。 初始化的时候也可以省略内部的花括号,只保留最外面的一对花括 号。只要保证数值的总个数正确,初始化效果就是一样的。如果数值的 个数不够,那么在数组初始化的时候,按照先后顺序来逐行赋值,因此 前面的元素首先得到赋值,直到没有数值为止。后面没有赋值的元素被 初始化为0。如图所示:
5 7 6 8 0 0 5 8 6 0 7 0

int a[2][3]={{5,6},{7,8}};

int a[2][3]={5,6,7,8};

六、初始化多维数组
由于数组rain中存放不应该被修改的数据,因此在声明数组时程序 使用了const修饰符。

七、更多维的数组
可以用如下方式声明三维数组:

int box[10][20][30];
可以这样理解:一维数组是排成一行的数据,二维数组是放在一个 平面上的数据,三维数组是把平面数据一层一层地垒起来。 通常处理二维数组时需要2重嵌套循环,处理3维数组需要3冲嵌套 循环,对于其它多维数组,依次类推。

八、数组基础练习
1、一维数组的基本操作
(1)数组的赋值、输入、输出

例1:按照顺序读入十个数据,以逆序方式输出。
(2)数组元素的移动

例2:将a数组中的第一个元素移到最后数组末尾,其余数据依次往前平 移一个位置。
(3)数组元素的查找、插入、删除

例3:对于数组a,输入一个测试数据x,如果x存在于数组a,则把x元素 删除;否则将x插在相应的位置,要求数组仍然有序(假设数组递增)。

八、数组基础练习
1、一维数组的基本操作
(4)找出数组元素中最大(或最小)的元素。

例4:从键盘输入若干个数,输出其中最大的数字。
(5)数组元素的排序。

例5:从键盘输入若干个数,将它们按照从小到大的顺序输出。

八、数组基础练习
2、二维数组的基本操作
二维数组的赋值、读入、输出

例1:输入50名学生5门课的考试成绩,输出个人各科成绩及总分。 例2:打印杨辉三角的前10行。杨辉三角如图所示:
1 1 1 1 2 1 1 1 1 1 2 1

1 3 3 1
1 4 6 4 1 1 5 10 10 5 1

1 3 3 1
1 4 6 4 1 1 5 10 10 5 1

八、数组基础练习
2、多维数组的基本操作
二维数组的赋值、读入、输出

例3:大部分元素是0的矩阵称为稀疏矩阵,假设有k个非零元素,则可 把稀疏矩阵用k*3的矩阵简记之,其中第一列是行号,第二列是列号, 第三列是该行、该列下的非零元素的值。如:

?0 ?2 ? ?0 ? ?0

0 0 1 0

0 0 0 0

5? ? 0? 0? ? 0?

?1 4 5 ? ?2 1 2? ? ? ? ?3 2 1 ? ?

八、数组基础练习
2、多维数组的基本操作
二维数组的赋值、读入、输出

例4:求n*n数阵中的马鞍数,输出它的位置。所谓马鞍数,是指在行上 最小而在列上最大的数。如下:(n=5)
5 4 3 2 1 6 5 4 3 2 7 6 5 4 5 8 7 2 9 4 9 8 1 0 8

则1行1列上的数 就是马鞍数。