C语言二维数组
2026/4/25 3:18:31 网站建设 项目流程

一、示例

int matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };

二、初始化

三、数组越界:

四、数组作为函数参数:

五、二维数组的使用

六、二维数组在内存中的存储

二维数组在内存中也是连续存储的。它遵循“行优先”的原则,即先存储第一行的所有元素,然后是第二行的所有元素,依此类推。

七、注意事项:

  • 数组名是地址:数组名本身代表数组首元素的地址(即&arr[0])。它是一个指针常量,不能被修改(例如arr = some_other_address;是非法的)。
  • 数组不能整体赋值/拷贝:你不能像arr1 = arr2;这样直接将一个数组赋值给另一个数组。必须使用循环或memcpy等函数逐个元素复制。
  • 数组大小sizeof(数组名)可以获取整个数组占用的字节数。对于静态数组,可以用sizeof(整个数组) / sizeof(数组中一个元素)来计算元素个数,但这只在数组定义的作用域内有效,不能用于函数参数中的数组。

八、二维数组的指针

1. 二维数组的内存布局

int matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };

这个matrix在内存中是连续存放的:1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12

每一行matrix[0],matrix[1],matrix[2]都可以看作是一个包含4个int一维数组

2. 二维数组名的衰变

当二维数组名matrix出现在表达式中时,它会衰变指向其第一行(即一个包含4个int的数组)的指针

  • matrix的类型是int [3][4](一个包含3行4列的二维数组)。
  • 当作为值使用时,matrix的值是&matrix[0],其类型是int (*)[4]。这个类型读作:“指向包含4个int元素的数组的指针”。
3. 声明和使用指向二维数组行的指针

我们可以显式地声明一个与二维数组名衰变后类型相同的指针。

示例代码:

#include <stdio.h> int main() { int matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; // pm 是一个指向包含4个int元素的数组的指针 int (*pm)[4] = matrix; // 等价于 int (*pm)[4] = &matrix[0]; printf("二维数组的地址 (matrix): %p\n", (void*)matrix); printf("pm 指向的地址: %p\n", (void*)pm); printf("matrix[0] 的地址 (&matrix[0]): %p\n", (void*)&matrix[0]); printf("pm 的大小 (指针大小): %zu 字节\n", sizeof(pm)); printf("(*pm) 的大小 (一行数组的大小): %zu 字节\n", sizeof(*pm)); // 4 * sizeof(int) // 如何访问元素? // pm 指向第一行 {1, 2, 3, 4} // *(pm + 0) 就是第一行 {1, 2, 3, 4},类型是 int[4] // *(pm + 1) 就是第二行 {5, 6, 7, 8},类型是 int[4] // *(pm + i) 就是第 i 行,类型是 int[4] // (*(pm + i))[j] 就是第 i 行第 j 列的元素 // pm[i][j] 是 *(pm + i)[j] 的简写形式,与二维数组的访问语法一致 printf("\n通过 pm[i][j] 访问:\n"); for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) { printf("%d ", pm[i][j]); // 等价于 (*(pm + i))[j] } printf("\n"); } // 指针算术 pm++; // pm 现在指向第二行 (matrix[1]) printf("\npm++ 后,pm 指向: %p\n", (void*)pm); printf("此时 pm[0][0] = %d\n", pm[0][0]); // pm[0][0] 等价于 matrix[1][0],输出 5 printf("此时 pm[0][1] = %d\n", pm[0][1]); // 输出 6 return 0; }
  • int (*pm)[4] = matrix;pm被初始化为指向matrix的第一行。
  • sizeof(*pm):返回*pm(即pm指向的整个行数组)的大小,这里是4 * sizeof(int)
  • pm[i][j]:这是访问二维数组的标准方式。pm + i计算出第i行的地址,*(pm + i)解引用得到第i行这个一维数组,最后[j]访问该行的第j个元素。
  • pm++:指针pm的步长是它所指向的对象的大小,即一行数组的大小4 * sizeof(int)个字节)。因此,pm++会让pm直接跳到下一行的起始地址。
4. 二维数组与函数参数

这是二维数组指针最重要的应用场景之一。

当我们将二维数组传递给函数时,数组的第一维大小可以省略,但第二维(列数)必须指定。这是因为函数需要知道每一行有多少个元素,才能正确地进行地址计算。

函数定义:

#include <stdio.h> // 方式1: 使用指针语法,明确表示指向行的指针 void printMatrix1(int rows, int (*matrix)[4]) { // 列数必须指定 printf("在 printMatrix1 中,matrix 的类型是 int(*)[4]\n"); for (int i = 0; i < rows; i++) { for (int j = 0; j < 4; j++) { printf("%d ", matrix[i][j]); // matrix[i][j] 等价于 *(*(matrix + i) + j) } printf("\n"); } } // 方式2: 使用数组语法,效果相同,更易读 void printMatrix2(int rows, int matrix[][4]) { // 列数必须指定 printf("在 printMatrix2 中,matrix 的类型也是 int(*)[4]\n"); for (int i = 0; i < rows; i++) { for (int j = 0; j < 4; j++) { printf("%d ", matrix[i][j]); } printf("\n"); } } // 方式3: 使用变长数组 (VLA) - C99 标准引入 void printMatrix3(int rows, int cols, int matrix[rows][cols]) { // 列数可以是变量 printf("在 printMatrix3 中,使用 VLA\n"); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { printf("%d ", matrix[i][j]); } printf("\n"); } } int main() { int my_matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; int rows = 3, cols = 4; printMatrix1(rows, my_matrix); // 传递二维数组名 printf("---\n"); printMatrix2(rows, my_matrix); printf("---\n"); printMatrix3(rows, cols, my_matrix); return 0; }

关键点

  • 列数必须指定:无论是int (*matrix)[4]还是int matrix[][4],方括号中的4都不能省略。这告诉编译器如何计算matrix[i]的地址(即第i行的地址)。
  • 第一维可以省略int matrix[][4]int matrix[3][4]在函数参数中是等价的,都表示一个指向包含4个int的数组的指针 (int (*)[4])。
  • 为什么这样设计:因为二维数组在内存中是连续的,编译器需要知道每行的宽度(列数)才能从第0行的地址计算出第1行、第2行...的地址。
5. 总结
  • 二维数组名arr在传递给函数或赋值给指针时,会衰变为指向其第一行的指针,类型为int (*)[列数]
  • 声明这种指针的语法是数据类型 (*指针名)[列数]
  • 这种指针p支持p[i][j]的访问方式,其原理是*(*(p + i) + j)
  • 指针算术p++的步长是一行数组的大小
  • 在函数参数中,二维数组必须指定列数,这是实现arr[i][j]随机访问的关键。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询