Skip to content

LV015-二维数组

一、多维数组

多维数组是一种特殊的数据结构,其数组元素本身仍是数组。这种递归嵌套的特性,使得数据能够以类似表格、矩阵乃至更高维度的形式进行组织。每个维度分别对应数据的不同属性层级,例如行、列以及更高阶的层次,从而让数据呈现更加清晰、有序的结构化特征。

多维数组根据维度数量可分为二维数组、三维数组、四维数组等。其中最常见的是二维数组,可以看作“表格”或“矩阵”。

img

二、二维数组

1. 怎么声明?

c
数据类型 数组名[ 常量表达式1 ][ 常量表达式2 ];

例如:

c
int a[2][3]; /* 定义了一个 2 行 3 列的二维数组 */

表示定义了一个 2 行 3 列的二维数组 ,元素共有 2x3 = 6 个( 元素个数 = 行数 x 列数 )。

【注意】

(1)声明时 列数不能省略行数可以省略(定义时进行初始化,编译器可以判别出行数时)

(2) sizeof(数组名) 可以获取整个二维数组所占据的字节数(等于元素个数 x 元素类型所占字节数)。

(3)sizeof(行数组名) 可以获取二维数组某行所占据的字节数(等于该行元素个数 x 元素类型所占字节数)。

2. 怎么访问?

2.1 访问格式

c
array_name[row_index][col_index]  /* 数组名 [行索引][列索引] 行索引和列索引都是从 0 开始*/
storage_type 存储类型(可以说明也可以不说明)
data_type 任意有效的 C 数据类型(必须说明)
p_name 指针变量名
#### 2.2 遍历二维数组
c
int a[2][3];
int i, j;
/* 通过 for 循环进行逐个元素的访问 */
for(i=0;i<2;i++)       /* 行循环 */
{
    for(j=0;j<3;j++)  /* 列循环 */
    {
       printf(“%d\t”, a[i][j]); 
    }
}

3. 元素初始化

c
int a[2][3] = {{1, 2, 3}, {4, 5, 6}}; /* 分行赋值 */
int a[2][3] = {1, 2, 3, 4, 5, 6};     /* 按数组排列顺序进行赋值 */
int a[2][3] = {{1}, {4, 5};           /* 对部分元素赋初值 */

【注意】

(1)与一维数组相同,数组不初始化,其元素值为随机;对 static 数组元素不赋初值,系统会自动赋以 0 值;只给部分数组元素赋初值,剩余元素自动赋值为 0 。

(2)当采用分行赋值的方法声明二维数组时,二维数组的行数可以省略,行数会由编译器自己进行计算。

【说明】

c
/* 声明形式 1 (两种写法等价) */
static int a[2][3];
static int a[2][3]; a[0][0] = 0; a[0][1] = 0; a[0][2] = 0; a[1][0] = 0; a[1][1] = 0; a[1][2] = 0;
    
/* 声明形式 2 (两种写法等价) */
int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
int a[2][3]; a[0][0] = 1; a[0][1] = 2; a[0][2] = 3; a[1][0] = 4; a[1][1] = 5; a[1][2] = 6;

/* 声明形式 3 (两种写法等价) */
int a[2][3] = {{1}, {4, 5}};
int a[2][3]; a[0][0] = 1; a[0][1] = 0; a[0][2] = 0; a[1][0] = 4; a[1][1] = 5; a[1][2] = 0;

/* 声明形式 4 (两种写法等价) */
int a[ ][3] = {{1, 2, 3}, {4, 5, 6}};
int a[2][3]; a[0][0] = 1; a[0][1] = 2; a[0][2] = 3; a[1][0] = 4; a[1][1] = 5; a[1][2] = 6;

/* 声明形式 5 (两种写法等价) */
int a[ ][3] = {{}, {4, 5}};
int a[2][3]; a[0][0] = 0; a[0][1] = 0; a[0][2] = 0; a[1][0] = 4; a[1][1] = 5; a[1][2] = 0;

/* 声明形式 6 (两种写法等价) */
int a[2][3] = {1, 2, 3, 4, 5, 6};
int a[2][3]; a[0][0] = 0; a[0][1] = 0; a[0][2] = 0; a[1][0] = 4; a[1][1] = 5; a[1][2] = 0;/* 编译器自己计算赋值,不过会有警告*/

/* 声明形式 7 (两种写法等价) */
int a[ ][3] = {1, 2, 3, 4, 5, 6};
int a[2][3]; a[0][0] = 0; a[0][1] = 0; a[0][2] = 0; a[1][0] = 4; a[1][1] = 5; a[1][2] = 0;/* 编译器自己计算赋值,不过会有警告*/

4. 存储空间

4.1 逻辑分析

逻辑层面看,我们常用矩阵形式(如 3 行 4 列)来表示二维数组,这种表示方法能够直观地体现出行与列之间的关系,便于我们理解和操作数据。然而,在计算机的内存世界里,二维数组并非以二维的形式存储,而是采用线性、连续的方式存放所有元素。

在 C 语言中,二维数组的元素是按照行优先的顺序进行排列的。也就是说,编译器会先顺序存放第一行的所有元素,紧接着存放第二行的所有元素,以此类推,直至将整个二维数组的所有元素都存放在连续的内存空间中。以一个 3 行 4 列的二维数组 a[3][4] 为例,其在内存中的存放顺序如下所示:

img

4.2 测试实例

二维数组在内存中是一维的,存储时 行序优先

c
#include <stdio.h>

int main(int argc, const char *argv[]) 
{
	int a[][3] = {{1, 2, 3}, {4, 5, 6}};
	int i, j;
	for(i = 0; i < 2; i++)
	{
		for(j = 0; j < 3; j++)
		{
			printf("&a[%d][%d] = %p, a[%d][%d] = %d\n",i, j, &a[i][j],i, j, a[i][j]);
		}
	}
	return 0;
}

image-20260210140055924

a[2][3] 在内存中的结构如下图所示, a[2][3] 数组为 int 类型,在 64 位平台下占 4 个字节,所以相邻元素之间地址相差 4 。

image-20260210141108836

5. 数组名

与一维数组一样,二维数组的数组名也代表了该二维数组的起始地址。二维数组的另一种理解方式:二维数组可以看做是由多个元素组成的一维数组,而每个元素又是一个数组,从而合起来构成了二维数组。例如:

c
#include <stdio.h>

int main(int argc, const char *argv[]) 
{
	int a[][3] = {{1, 2, 3}, {4, 5, 6}};
	int i, j;
	printf("a = %p\n", a);
	for(i = 0; i < 2; i++)
	{
		printf("a + %d = %p, a[%d] = %p\n",i,a +i, i, a[i]);
		for(j = 0; j < 3; j++)
		{
			printf("&a[%d][%d] = %p, a[%d][%d] = %d\n",i, j, &a[i][j],i, j, a[i][j]);
		}
	}
	return 0;
}

image-20260210141326039

经过打印地址发现:

(1) a 为整个二维数组的数组名,代表了整个二维数组的起始地址。

(2) a [0], a [1] 也分别代表了第一行和第二行起始数据的地址。

image-20260210142422760

经过分析和验证,可以得到:一个二维数组,按行可以分为多个一维数组,以 int a[2][3] 为例,该 2 行 3 列 的二维数组就可以理解为 2 个元素 组成,每个元素都是一个一维数组组成,每个一维数组的数组名就是二维数组名加上第一个下标,即 a [0], a [1] 。

行名(代表了地址) 每行元素
a a [0] &a [0][0] a [0][0] a [0][1] a [0][2]
a + 1 a [1] &a [1][0] a [1][0] a [1][1] a [1][2]
【注意】数组名 **不可以进行自加自减** 运算,自然这里的行名就相当于每一行元素的数组名,也不可以进行自加自减运算。

三、数组元素个数

1. 计算元素个数

我们有的时候需要程序去自动计算数组的长度,有下边两种方式:

c
len = sizeof(数组名)/sizeof(数组类型);
len = sizeof(数组名)/sizeof(数组名[0]);

已知数组元素类型的时候,我们可以使用第一种方式,当未知数组元素类型的时候,我们可以使用后边这种方式,直接使用数组中首个元素作为每个元素大小计算的标准。

2. 计算行数或者列数

行数或者列数至少要有 1 个已知才行。我们可以先计算出总元素个数,然后可以除以已知的行数或者列数就可以得到另一个未知的参数了。

参考资料:

25 字符数组与字符串及多维数组详解:定义与初始化、访问与遍历、%s 格式符、内存剖析、编程实战_多维字符串数组怎么定义-CSDN博客