C语言指针概念详解:数组指针与二级指针的本质区别

前言

最近考研复试在复习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语言的语法规则,但这并不改变数组本身的类型。数组指针指向的始终是数组,而不是指针变量。

相关推荐
为何创造硅基生物8 小时前
C语言 结构体内存对齐规则(通俗易懂版)
c语言·开发语言
仰泳之鹅8 小时前
【C语言】自定义数据类型2——联合体与枚举
c语言·开发语言·算法
jolimark9 小时前
C语言自学攻略:小白入门三步走
c语言·编程入门·学习路线·实践项目·自学攻略
cen__y10 小时前
Linux12(Git01)
linux·运维·服务器·c语言·开发语言·git
社交怪人10 小时前
【算平均分】信息学奥赛一本通C语言解法(题号2071)
c语言·开发语言
卢锡荣11 小时前
单芯通吃,盲插标杆 —— 乐得瑞 LDR6020,Type‑C 全场景互联 “智慧芯”
c语言·开发语言·计算机外设
AI科技星12 小时前
《数学公理体系·第三部·数术几何》(2026 年版)
c语言·开发语言·线性代数·算法·矩阵·量子计算·agi
kkeeper~12 小时前
0基础C语言积跬步之字符函数与字符串函数(上)
c语言·开发语言
東隅已逝,桑榆非晚13 小时前
字符函数和字符串函数
c语言·笔记
AI科技星16 小时前
第二章 平行素数对网格:矩形→等腰梯形拓扑变换(完整公理终稿)
c语言·开发语言·线性代数·算法·量子计算·agi