11.2 多维数组

C语言学习栏目目录

目录

1 初始化二维数组

2 其他多维数组


源码,编译环境VS2019

假设你是一个气象分析员,要分析5年内每个月的降水量数据,你首先要解决的问题是如何表示数据。一个方案是创建60个变量,每个变量储存一个数据项(我们曾经提到过这一笨拙的方案,和以前一样,这个方案并不合适)。使用一个内含60个元素的数组比将建60个变量好,但是如果能把各年的数据分开储存会更好,即创建5个数组,每个数组12个元素。然而,这样做也很麻烦,如果Tempest决定研究50年的降水量,岂不是要创建50个数组。是否能有更好的方案?处理这种情况应该使用数组的数组。主数组(master array)有5个元素(每个元素表示一年),每个元素是内含12个元素的数组(每个元素表示一个月)。下面是该数组的声明:

float rain[5][12]; // 内含5个数组元素的数组,每个数组元素内含12个float类型的元素

理解该声明的一种方法是,先查看中间部分:

float rain[5][12]; // rain是一个内含5个元素的数组

这说明数组rain有5个元素,至于每个元素的情况,要查看声明的其余部分:

floatrain[5][12] ; // 一个内含12个float类型元素的数组

这说明每个元素的类型是float[12],也就是说,rain的每个元素本身都是一个内含12个float类型值的数组。

根据以上分析可知,rain的首元素rain[0]是一个内含12个float类型值的数组。所以,rain[1]、rain[2]等也是如此。如果 rain[0]是一个数组,那么它的首元素就是 rain[0][0],第 2 个元素是rain[0][1],以此类推。简而言之,数组rain有5个元素,每个元素都是内含12个float类型元素的数组,rain[0]是内含12个float值的数组,rain[0][0]是一个float类型的值。假设要访问位于2行3列的值,则使用rain[2][3](记住,数组元素的编号从0开始,所以2行指的是第3行)。

该二维视图有助于帮助读者理解二维数组的两个下标。在计算机内部,这样的数组是按顺序储存的,从第1个内含12个元素的数组开始,然后是第2个内含12个元素的数组,以此类推。我们要在气象分析程序中用到这个二维数组。该程序的目标是,计算每年的总降水量、年平均降水量和月平均降水量。要计算年总降水量,必须对一行数据求和;要计算某月份的平均降水量,必须对一列数据求和。二维数组很直观,实现这些操作也很容易。下程序清单演示了这个程序。

/************************************************************************
功能:  计算每年的总降水量、年平均降水量和5年中每月的平均降水量
/************************************************************************/

#include<stdio.h>
#define MONTHS 12   // 一年的月份数
#define YEARS 5     // 年数
int main(void)
{
	// 用2010~2014年的降水量数据初始化数组
	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("年降雨量(英寸)\n");
	for (year  = 0, total  = 0; year  < YEARS; year++)
	{            // 每一年,各月的降水量总和
		for (month  = 0, subtot  = 0; month  < MONTHS; month++)
			subtot  += rain[year][month];
		printf("%5d %15.1f\n", 2010 + year, subtot);
		total += subtot; // 5年的总降水量
	}
	printf("\n年平均值为 %.1f 英寸.\n\n", total /	YEARS);
	printf("月平均值:\n\n");
	printf(" 一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月\n");
	for (month  = 0; month  < MONTHS; month++)
	{            // 每个月,5年的总降水量
		for (year  = 0, subtot  = 0; year  < YEARS; year++)
			subtot  += rain[year][month];
		printf("%4.1f ", subtot  / YEARS);
	}
	printf("\n");
	return 0;
} 

下面是该程序的输出:

年降雨量(英寸)
 2010            32.4
 2011            37.9
 2012            49.8
 2013            44.0
 2014            32.9

年平均值为 39.4 英寸.

月平均值:

 一月 二月 三月 四月 五月 六月 七月 八月 九月 十月 十一月 十二月
 7.3  7.3  4.9  3.0  2.3  0.6  1.2  0.3  0.5  1.7  3.6  6.7

学习该程序的重点是数组初始化和计算方案。初始化二维数组比较复杂,我们先来看较为简单的计算部分。程序使用了两个嵌套for循环。第1个嵌套for循环的内层循环,在year不变的情况下,遍历month计算某年的总降水量;而外层循环,改变year的值,重复遍历month,计算5年的总降水量。这种嵌套循环结构常用于处理二维数组,一个循环处理数组的第1个下标,另一个循环处理数组的第2个下标:

for (year = 0, total = 0; year < YEARS; year++)
{ 
    // 处理每一年的数据
    for (month = 0, subtot = 0; month < MONTHS; month++)
    ...// 处理每月的数据
    ...//处理每一年的数据
}

第2个嵌套for循环和第1个的结构相同,但是内层循环遍历year,外层循环遍历month。记住,每执行一次外层循环,就完整遍历一次内层循环。因此,在改变月份之前,先遍历完年,得到某月 5 年间的平均降水量,以此类推:

for (month = 0; month < MONTHS; month++)
{
    // 处理每月的数据
    for (year = 0, subtot =0; year < YEARS; year++)
    ...// 处理每年的数据
    ...// 处理每月的数据
}

1 初始化二维数组

初始化二维数组是建立在初始化一维数组的基础上。首先,初始化一维数组如下:

sometype ar1[5] = {val1, val2, val3, val4, val5};

这里,val1、val2等表示sometype类型的值。例如,如果sometype是int,那么val1可能是7;如果sometype是double,那么val1可能是11.34,诸如此类。但是rain是一个内含5个元素的数组,每个元素又是内含12个float类型元素的数组。所以,对rain而言,val1应该包含12个值,用于初始化内含12个float类型元素的一维数组,如下所示:

{4.3,4.3,4.3,3.0,2.0,1.2,0.2,0.2,0.4,2.4,3.5,6.6}

也就是说,如果sometype是一个内含12个double类型元素的数组,那么val1就是一个由12个double类型值构成的数值列表。因此,为了初始化二维数组rain,要用逗号分隔5个这样的数值列表:

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个数值列表,每个数值列表都用花括号括起来。第1个列表的数据用于初始化数组的第1行,第2个列表的数据用于初始化数组的第2行,以此类推。前面讨论的数据个数和数组大小不匹配的问题同样适用于这里的每一行。也就是说,如果第1个列表中只有10个数,则只会初始化数组第1行的前10个元素,而最后两个元素将被默认初始化为0。如果某列表中的数值个数超出了数组每行的元素个数则会出错,但是这并不会影响其他行的初始化。

初始化时也可省略内部的花括号,只保留最外面的一对花括号。只要保证初始化的数值个数正确,初始化的效果与上面相同。但是如果初始化的数值不够,则按照先后顺序逐行初始化,直到用完所有的值。后面没有值初始化的元素被统一初始化为0。下图演示了这种初始化数组的方法。

因为储存在数组rain中的数据不能修改,所以程序使用了const关键字声明该数组。

2 其他多维数组

前面讨论的二维数组的相关内容都适用于三维数组或更多维的数组。可以这样声明一个三维数组:

int box[10][20][30];

可以把一维数组想象成一行数据,把二维数组想象成数据表,把三维数组想象成一叠数据表。例如,把上面声明的三维数组box想象成由10个二维数组(每个二维数组都是20行30列)堆叠起来。还有一种理解box的方法是,把box看作数组的数组。也就是说,box内含10个元素,每个元素是内含20个元素的数组,这20个数组元素中的每个元素是内含30个元素的数组。或者,可以简单地根据所需的下标值去理解数组。

通常,处理三维数组要使用3重嵌套循环,处理四维数组要使用4重嵌套循环。对于其他多维数组,以此类推。在后面的程序示例中,我们只使用二维数组。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值