【C语言干货】二维数组传参本质
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、二维数组的内存布局
- 1.二维数组的实质
- 2.二维数组的地址关系
- 二、二维数组传参的本质
- 1.参数传递的退化机制
- 2.三种等效的函数声明方式
- 总结
前言
提示:以下是本篇文章正文内容,下面案例可供参考
一、二维数组的内存布局
1.1 二维数组的实质
二维数组本质上是一个"数组的数组",即每个元素本身又是一个数组。例如:
int arr[3][4] = {{1, 2, 3, 4},{5, 6, 7, 8},{9, 10, 11, 12}
};
在内存中,二维数组是按行优先顺序连续存储的,实际内存布局为:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
1.2 二维数组的地址关系
对于上述arr[3][4]
:
-
arr
是二维数组名,类型是int (*)[4]
(指向包含4个int的数组的指针) -
arr[0]
是第一行一维数组的数组名,类型是int *
-
&arr[0][0]
是第一个元素的地址,类型是int *
printf("arr: %p\n", (void*)arr);
printf("arr+1: %p\n", (void*)(arr+1)); // 跳过一行(16字节)
printf("arr[0]: %p\n", (void*)arr[0]);
printf("arr[0]+1: %p\n", (void*)(arr[0]+1)); // 跳过一个元素(4字节)
printf("&arr[0][0]: %p\n", (void*)&arr[0][0]);
二、二维数组传参的本质
2.1 参数传递的退化机制
当二维数组作为函数参数传递时,会发生"数组到指针"的退化(decay):
-
第一维会退化为指针
-
第二维必须明确指定大小
-
传递的实际上是第一行的地址
2.2 三种等效的函数声明方式
方式一:完整二维数组形式
void func(int arr[3][4], int rows, int cols);
方式二:省略第一维大小
void func(int arr[][4], int rows, int cols);
方式三:数组指针形式
void func(int (*arr)[4], int rows, int cols);
这三种声明在编译器看来完全等价,都会被视为int (*)[4]
类型的参数。
总结
1 核心要点总结
-
二维数组传参本质是传递指向第一行的指针
-
必须指定第二维的大小,以便编译器计算行偏移
-
三种声明方式完全等价,推荐使用
int (*arr)[N]
形式以明确指针本质 -
动态分配的"二维数组"需要不同的传参方式
-
C99变长数组提供了更灵活的解决方案
2 最佳实践建议
-
对于固定大小的二维数组,使用数组指针形式传参:
void func(int (*arr)[4], int rows);
-
对于动态分配的二维数组,使用二级指针传参:
void func(int **arr, int rows, int cols);
-
在C99及以上环境中,考虑使用变长数组语法提高灵活性
-
始终传递行数和列数作为额外参数,避免在函数内部尝试获取数组大小
-
如果函数不修改数组内容,添加const修饰符:
void func(const int (*arr)[4], int rows);
理解二维数组传参的本质,关键在于认识其内存布局和"数组到指针"的退化机制。掌握了这些原理,你就能游刃有余地处理各种二维数组相关的函数设计了。