
前一章我们具体了解了C语言中最为精妙和强大的存储艺术 – 数组!并且知道如何运用单排书架,整齐排列同类卷轴的一维数组,接下来让我们来了解并掌握如同整个图书馆,构建行与列的魔法矩阵的多维数组。
前面学习的数组为一维数组,数组的元素都是内置类型的。如果我们把一维数组作为数组的元素,那么由一维数组组成的数组被称为二维数组。同理,二维数组作为数组元素的数组称为三维数组,二维数组以上的数组统称为多维数组。

那我们如何定义二维数组呢?语法如下:
type arr_name[常量值1][常量值2]
| | | |
类型 数组名 行 列例如:
int arr[3][5];
double date[6][8];解释:上述代码中出现的信息
在创建变量或者数组的时候,给定一些初始化,被称为初始化。
那二维数组如何初始化呢?像一维数组一样,也是大括号初始化。
int arr1[3][4] = { 1,2 };
int arr2[3][4] = { 0 };二维数组是逐行初始化,如果是不完全初始化,剩余的元素默认初始化为0。

int arr3[3][4] = { 1,2,3,4, 2,3,4,5, 3,4,5,6 };
int arr4[3][4] = {{1,2},{3,4},{5,6}};
int arr5[][4] = { 1,2,3 };
int arr6[][4] = { 1,2,3,4,5,6 };
int arr7[][4] = {{1,2},{3,4},{5,6}};
当我们掌握了二维数组的创建和初始化,那么我们怎么使用二维数组呢?
其实二维数组访问也是使用下标的形式,二维数组是有行和列,就像找书文一样,只要锁定了行和列,就能锁定数组中的一个元素。
C语言中规定,二维数组的行和列都是从0开始的,如下所示:
int arr[3][4] = { 1,2,3,4, 2,3,4,5, 3,4,5,6 };
图中最右侧竖着一列数字表示行号,第一行数字表示列号,都是从0开始,比如:第1行,第2列,就能快速定位出4。
int main()
{
int arr[3][4] = { 1,2,3,4, 2,3,4,5, 3,4,5,6 };
printf("%d\n", arr[2][1]);
return 0;
}输出结果如下:

如何从一排书架中(二维数组)找到想要的(书籍)我们已经知道了,那如何访问整个二维数组呢?
其实我们只要按照一定的规律产生所有的行和列就行。以上面一段代码中的arr为例,行的选择范围是0 ~ 2,;列的取值范围是0 ~ 4,所以我们可以借助循环实现生成所有下标。
int main()
{
int arr[3][4] = { 1,2,3,4, 2,3,4,5, 3,4,5,6 };
int i = 0;//遍历行
//输出
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
int main()
{
int arr[3][4] = { 1,2,3,4, 2,3,4,5, 3,4,5,6 };
int i = 0;//遍历行
//输入
for (i = 0; i < 3; i++)//产生行号
{
int j = 0;
for (j = 0; j < 4; j++)//产生列号
{
scanf("%d", &arr[i][j]);//输入数据
}
}
//输入
for (i = 0; i < 3; i++)//产生行号
{
int j = 0;
for (j = 0; j < 4; j++)//产生列号
{
printf("%d ", arr[i][j]);//输出数据
}
printf("\n");
}
return 0;
}
像一维数组一样,我们如果想研究二维数组在内存中的存储方式,我们也可以打印出相关的地址。
代码如下:
int main()
{
int arr[3][4] = { 0 };
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
printf("&arr[%d][%d] = %p\n", i, j, &arr[i][j]);
}
}
return 0;
}输出结果如下:

从输出结果来看,每一行内部的每一个元素都是相邻的,地址之间相差4个字节,跨行位置处的两个元素(如:arr[0][4]和arr[1][0])之间也是差4个字节,所以二维数组中的每个都是连续存放的。
如下图所示:

了解清楚二维数组在内存中的布局,有利于后期使用指针来访问数组的学习。
在C99标准之前,C语言在创建数组的时候,数组大小的指定只能使用常量、常量表达式,或者如果我们初始化数据的话,可以省略数组大小。
如:
int arr1[10];
int arr2[3+5];
int arr3[] = {1,2,3};虽然没有直接指定数组的大小,编译器会根据数组的初始化内容来确定一个数组的大小。
这样的语法限制,让我们创建数组就会不够灵活,有时候数组大了浪费空间,有时侯数组有小了不够用。
C99中给一个**变长数组(variable-length array,简称VLA)**的新特性,允许我们可以使用变量指定数组大小。
请看下面的代码:
int n = a + b;
int arr[n];上面示例中,数组arr就是变长数组,因为它的长度取决于变量n的值,编译器没法事先确定,只有运行时才知道n是多少,所以变长数组不能初始化。
不过在vs 2022会面临无法编译问题,可以改一下编译器,右键单击解决方案,然后属性


之后可以测试一下
int main()
{
int n = 0;
scanf("%d", &n);
int arr[i];
int i = 0;
for (i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
for (i = 0; i < n; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}有开始可知,三维数组的元素类型是二维数组,同二维数组一般,可以带入一下。
代码如下:
int main()
{
int arr[3][4][2] = { {{1,1},{1,2},{1,3},{1,4}},{2,1,3,4,0,5},{{3,1},{0},{1},{2,4}} };
int i = 0;
int j = 0;
int k = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
for (k = 0; k < 2; k++)
{
printf("%3d ", arr[i][j][k]);
}
printf("\n");
}
printf("\n\n");
}
return 0;
}
剩下其他维度的数组可以类推得到。
回顾这段旅程,我们见证了一维数组如何将散落的魔法卷轴整齐归位,多维数组如何建行列分明的魔法图书馆,sizeof操作符如何精确每个魔法容器的尺寸…
数组教会我们不仅是技术,更是一种思维方式—将复杂问题分解为有序元素,用索引而非重复来管理数据。
愿数组的力量与你同在!