例如,气象员要分析5年中每月的降水量数据,首先需要解决的问题是如何表示这些数据。可以使用60个变量。或者使用一个60个元素的数组,或者使用5个数组,每个数组12个元素。这些方法都比较笨拙,而且如果要处理的数据是50年,这些方法就很不合适。我们需要找到一种更好的方法。
更好的处理方法是使用一个数组的数组,即:主数组包含5个元素,每个元素代表一年。代表一年的元素是包含12个元素的数组。这种数组的数组,我们称之为二维数组。下面是这种数组的声明方法:
float rain [5][12]; //5个由12个浮点数组成 的数组 的数组
理解这个声明的一种方法是首先查看位于中间的那部分:
float rain[5][12]; //rain是一个包含5个元素的数组
这部分说明rain是一个包含5个元素的数组。至于每个元素的情况,需要查看声明的其余部分。
float rain[5][12]; //12个浮点数的数组
这说明每个元素的类型是float [12];也就是说,rain具有5个元素,并且每一个元素都是包含12个float数值的数组。
按此类推,rain的首元素rain[0]是一个包含12个float数值的数组。rain[1],rain[2]等待也是如此。rain[0]是数组,那么它的首元素是rain[0][0],第二个元素是rain[0][1],依此类推其他元素。简单地说,rain是一个包含5个元素(每个元素又是包含12个float数的数组)的数组,rain[0]是一个包含12个float数的数组,rain[0][0]是一个float数。
也可以把rain数组看作是一个二维数组,它包含5行,每行12个列。改变第二个下标,可以沿着一行移动,每移动一个单位代表一个月份。改变第一个下标,可以沿着一列垂直移动,每移动一个单位代表一年。
用二维视图表示数组便于我们直观地想象具有两个索引的数组。实际上,数组是顺序存储的,前12个元素之后,跟着就是第二个包含12个元素的数组,依次类推。
我们将在气象分析程序中使用这个二维数组,程序的目的是计算年降水总量,年降水平均量和月降水平均量。
程序清单10.7 rain.c程序
/*rain.c --针对若干年的降水量数据,计算年降水问题、年降水平均量、以及月降水平均量*/
#include <stdio.h>
#define MONTHS 12
#define YEARS 5
int main (void)
{
//把数组初始化为2000年到2004年的降水量数据
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}
};
int year,month;
float subtot,total;
printf(" YEAR RAINFALL (inches) \n");
for (year = 0,total = 0;year<YEARS;year++)
{ //对于每一年各月的总降水量
for(month=0,subtot=0;month<MONTHS;month++)
subtot+=rain[year][month];
printf("%5d %15.lf\n",2000+year,subtot);
total+=subtot; //所有年度的总降水量
}
printf("\nThe yearly average is %.1f inches.\n\n",total/YEARS);
printf("MONTHLY AVERAGES: \n\n");
printf("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\n");
for (month=0;month<MONTHS;month++)
{ //对于每个月,各年该月份的总降水量
for(year=0,subtot=0;year<YEARS;year++)
subtot+=rain[year][month];
printf("%4.1f",subtot/YEARS);
}
printf("\n");
return 0;
}
研究此程序时,请注意数组的初始化方法和计算方案。而数组的初始化是其中较复杂 的部分,我们先来研究相对简单的部分(计算)。
要计算某年度的降水总量,则保持year为常量,让month遍历整个范围,这正是程序的第一部分的内部for循环的作用。程序第一部分外部循环的目的则是让变量year在值域(5年)内遍历。像这样的嵌套循环结构在处理二维数组时是比较方便的。利用一个循环处理第一个下标,利用另一个循环处理第二个下标。
程序第二部分的结构和第一部分相同,但year被改变为内部循环,而month被改变为外部循环。注意,外部循环每执行一次,内部循环完整遍历一次。因此,在月份改变之前,年度先遍历。先得到的是5年中一月份的降水平均量,然后依次类推。
10.2.1 初始化二维数组
对二维数组的初始化是建立在对一维数组的初始化之上的。
前面讨论的数据个数和数组大小不匹配的问题同样适用二维数组。也就是说,如果第一个列表 中有10个数值,则第一行只有前10个元素得到赋值,最后两个元素被默认初始化为0。如果列表中的数值多于12个,则报告错误;而且这些数值不会影响到下一行的赋值。
初始化的时候可以省略内部的花括号,只保留一对外部的花括号。只要保证数值的个数正确,初始的效果就是一样的。如果数值的个数不够,那么在数组初始化的时候,按照先后顺序来逐行赋值,因此前面的元素首先得到赋值,直到没有数值为止。后面没有赋值的元素被初始化为0。
10.2.2 更多维数的数组
前面关于二维数组的讨论对于三维乃至更多维数组同样适用。可以用下面的方式声明三维数组:
int box[10][20][30];
可以这样直观地理解,一维数组是排成一行的数据,二维数组是放在一个平面上的数据,三维数组是把平面数据一层一层垒起来。例如,可以把上面定义的数组 box直观想象为数据构成的方块:由10个二维数组(每个二维数组都是20行30列)堆放起来构成的立方体。
另一种理解box的方法认为它是数组的数组的数组。即box是包含10个元素的数组,其中每个元素又是包含20个元素的数组,这20个元素中的每一个又是包含30个元素的数组。或者可以简单地按照所需的索引数目去理解数组。
通常处理三维数组需要3重嵌套循环,处理四维数组需要4重嵌套循环,对于其他多维数组,依此类推。