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

前言

最近考研复试在复习C语言,突然看到以前初学时没有深究的一个知识点:数组指针与指针数组(int (*p)5 vs int *p5

之前一直以为这它们虽然一个本质是指针,一个本质是数组,但是二者同为二级指针,可以相互转换。今天查阅资料并与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语言的语法规则,但这并不改变数组本身的类型。数组指针指向的始终是数组,而不是指针变量。

相关推荐
2601_954526751 小时前
【工业传感与算法实战】温漂补偿与零点抗漂破局:基于二阶多项式拟合的 C/C++ 边缘校准算法,深度拆解“压力变送器什么牌子好”的技术硬指标
c语言·c++·算法
qq_448011161 小时前
C语言中的变量和函数的定义与声明
android·c语言·开发语言
aaaameliaaa5 小时前
指针之总结
c语言·笔记·算法
agathakuan5 小时前
Wireshark 解密並導出TLS 1.2 / TLS 1.3 明文的方法(可控制 Client 端)
c语言·wireshark·ssl·openwrt
caimouse5 小时前
protoc-gen-c 支持 proto3 `optional` 关键字修改记录
c语言·学习
noipp5 小时前
推荐题目:洛谷 P6231 [JSOI2013] 公交系统
c语言·数据结构·c++·算法·游戏·洛谷·luogu
c238566 小时前
C/C++每日一练6
c语言·c++·算法
wdfk_prog6 小时前
嵌入式面试真题第 13 题:单硬件 Timer 下的高精度多路软件定时器架构设计
c语言·开发语言·面试·职场和发展·嵌入式
时间的拾荒人7 小时前
MySQL C语言连接 - 从入门到实战
android·c语言·mysql
code_pgf9 小时前
C/C++ 中 `typedef` 关键字详解
c语言·c++