前言
最近考研复试在复习C语言,突然看到以前初学时没有深究的一个知识点:数组指针与指针数组(int (*p)[5] vs int *p[5])
之前一直以为这它们虽然一个本质是指针,一个本质是数组,但是二者同为二级指针,可以相互转换。今天查阅资料并与AI深度交流,才终于弄懂二者的区别。
先说结论: 数组指针本质是仍然是一级指针,虽然数组指针指向数组(数组名在某些场景如函数,可以退化为指针)但是数组虽然会退化,但本身不是指针变量。
而指针数组,它本质是一个数组,每个数组元素存储一个变量地址,且指针数组在函数参数其实就等价为二级指针。
更多补充: 指针数组vs数组指针,即各自函数传参
++因此探讨的核心即为:数组指针与二级指针的本质区别++
核心问题:为什么 char (*p)[5] 不是二级指针?
你的困惑很有代表性,关键在于理解数组名的双重身份 和指针指向的目标类型。让我们一步步拆解:
1. 数组名的双重身份
数组名在C语言中有两个不同的身份:
身份1:数组整体的标识符
char arr[5] = "hello";
printf("&arr = %p\n", &arr); // 数组整体的地址(类型:char (*)[5])
&arr是整个数组的地址 ,类型是char (*)[5](指向长度为5的char数组的指针)- 这是一个具体的内存位置,可以被存储在数组指针中
身份2:数组首元素的地址(数组名退化)
char arr[5] = "hello";
printf("arr = %p\n", arr); // 首元素地址(类型:char*)
printf("&arr[0] = %p\n", &arr[0]); // 和上面完全相同
- 当数组名单独使用 时(如作为函数参数),会自动退化为指向首元素的指针
- 此时
arr等价于&arr[0],类型是char*
2. 关键:数组指针指向的是什么?
char arr[3][5] = {"abc", "def", "ghi"};
char (*p)[5] = arr; // arr退化后是 char (*)[5] 类型
分析 p 的指向:
p存储的是arr[0](第一行数组)的地址arr[0]是一个长度为5的char数组(不是指针变量)- 因此
p是一个指向数组的指针,不是指向指针的指针
内存布局对比:
情况1:二维数组(char arr[3][5])
内存中是连续的字符序列:
地址: | 0x100 | 0x101 | 0x102 | 0x103 | 0x104 | 0x105 | 0x106 | ...
内容: | 'a' | 'b' | 'c' | '\0' | ? | 'd' | 'e' | ...
|<---- arr[0] ---->| |<---- arr[1] ---->|
arr[0]是数组,不是指针变量p指向arr[0]的起始地址
情况2:指针数组(char *arr[3])
内存中是指针变量的数组:
地址: | 0x100 | 0x101 | 0x102 | 0x103 | 0x104 | 0x105 | 0x106 | ...
内容: | 0x200 | 0x210 | 0x220 | ? | ? | ? | ? | ...
|<---- arr[0] ---->| |<---- arr[1] ---->|
arr[0]是指针变量,存储字符串的地址(如0x200)char **p = arr中,p指向arr[0](指针变量)
3. 为什么 char (*p)[5] 不是二级指针?
二级指针的定义是:指向指针变量的指针
反证法:
如果 char (*p)[5] 是二级指针,那么:
*p应该是一个指针变量**p应该能访问到最终数据
但实际情况:
char arr[3][5] = {"abc", "def", "ghi"};
char (*p)[5] = arr;
printf("*p = %p\n", *p); // *p 是 arr[0](第一行数组)的地址
printf("**p = %c\n", **p); // **p 等价于 *(*p + 0) = *arr[0] = arr[0][0] = 'a'
*p不是指针变量,而是arr[0](数组)的地址**p访问的是arr[0][0](第一个字符),不是通过两次解引用指针变量
4. 总结:本质区别
| 指针类型 | 存储的地址 | 指向的目标类型 | 是否是二级指针 | |---------|------------|---------------|---------------| | char **p | 指针变量的地址 | char*(指针) | ✅ 是 | | char (*p)[5] | 数组的地址 | char[5](数组) | ❌ 不是 |
核心结论:
- 二级指针的关键是「指向指针变量」
- 数组指针指向的是「数组」,数组虽然会退化,但本身不是指针变量
- 判断是否为二级指针,要看指向的目标类型,而不是语法结构或数组名的退化特性
记住:数组名的退化是C语言的语法规则,但这并不改变数组本身的类型。数组指针指向的始终是数组,而不是指针变量。