前言:这个问题倒没什么难的,就是绕,想的多了思路一不小心就拧一块去了(
1.引子
对于下面这一串代码(无法正常运行),可能部分同学曾经写过,并奇怪为何无法运行,即如标题所说——int**arr 不能用于二维数组传参(但可以用于指针数组传参)
void test(int** arr) {
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
printf("%d ", arr[i][j]);
return;
}
int main() {
int arr1[2][2] = { 1,2,3,4 };//二维数组
test(arr1);
return 0;
}
正常写法如下
//指针数组传参
void test(int** arr) {
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
printf("%d ", *(*(arr + i) + j)/*等效于arr[i][j]*/);
return;
}
int main() {
int arr1[] = { 1,2 }; int arr2[] = { 2,3 };
int* arr[] = { arr1,arr2 };//指针数组
test(arr);
return 0;
}
2.原因
- 解引用 * 对于 二维数组 和 指针 的规则不太一样
int main() {
int a = 0; int* pa = &a; int** ppa = &pa;
int arr[3][3] = {0};
//ppa为二级指针,*ppa 为一级指针,**ppa为数据 ——指针指向什么解出来就是什么
//arr为二维数组首元素地址(指向其中的第一个一维数组),*arr 即对第一个一维数组的地址解引用,得到其中(这个一维数组)的首元素地址 ——数组……自己看吧,就挺难概括表达的
//*(*arr)为元素
return 0;
}
- 关于传参的问题
函数内的形参类型取决于你定义的形参类型,与传入的实参类型无关
也就是说:你传入函数的实参代表整个数组也好,只是一个普通的指针也罢,在进入形参为 int** arr 的函数后都会被识别为二级指针,进入形参为 int arr[][4] 的函数后都会被识别为一个指向这样的二维数组的指针
于是就有下面这样的代码:
#include<stdio.h>
void test(int arr[][2]) {//定义形参arr为这样一个二维数组的数组名——所以 arr 指向二维数组首元素(其中的首个一维数组)的地址
printf("%d ", *arr); /*如上 1 所述,解引用后仍应是地址*/ printf("<=> %p\n", *arr);
printf("%d\n", *(*arr));//如上 1 所述,得到数据
}
int main() {
int a = 1;
int* pa = &a;
test(pa);//传入的实参只是个普通的指针
return 0;
}
2023.11.14 补充(就是想补充一下而已):
int (*arr)[][2] 和 int arr[][2] 做参数时,arr 的含义是不一样的
前者 arr 指向整个二维数组;后者 arr 就是个二维数组名,指向二维数组首元素(即其中首个(整个)一维数组)
也就是前者解引用才能得到后者
我们以前者为例:
#include<stdio.h>
void test(int (*arr)[][2]) { //★ 参数含义为:arr 指向整个二维数组
printf("%d <=> %p\n", *arr, *arr); //解引用,得到首元素地址,*arr 为指向其中首个(整个)一维数组的指针
printf("%d <=> %p\n", **arr, **arr);//解引用,得到一维数组首元素地址
printf("%d\n", ***arr); //得到数据
}
int main() {
int arr[2][2] = { 1,2,3,4 };
test(arr);
return 0;
}
因而对于开头的问题我们可以得到如下逻辑:
验证:根据错误实例的推断,我们可以知道,对形参只用一次解引用即得数据
void test(int** arr) {
for (int i = 0; i < 2; i++) for (int j = 0; j < 2; j++) printf("%d ", *arr);//
return;
}
int main() {
int arr1[2][2] = { 1,2,3,4 };
test(arr1);
return 0;
}
2024.1.10 补:VS 的 .c 文件允许以上的写法,但 .cpp 文件会说 “int (*)[2]” 类型的实参与 “int **” 类型的形参不兼容,报错 —— 编译器真是奇妙 ……