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

相关推荐
senijusene4 小时前
用C语言制作一个简易HTTP服务器:实现手机商城用户认证与搜索
服务器·c语言·http
Aaswk4 小时前
蓝桥杯2025年第十六届省赛真题(更新中)
c语言·数据结构·c++·算法·职场和发展·蓝桥杯
香水5只用六神4 小时前
【DMA】存储器到外设模式实验2
c语言·git·stm32·单片机·嵌入式硬件·github·visual studio
Yupureki5 小时前
《C++实战项目-高并发内存池》4.CentralCache构造
c语言·开发语言·c++·单例模式·github
xh didida5 小时前
数据结构--实现链式结构二叉树
c语言·数据结构·算法
祈安_6 小时前
动态内存管理(上)
c语言·后端
进击的小头6 小时前
第8篇:PI控制器设计实战演练
c语言·python·mcu·算法
Yupureki7 小时前
《C++实战项目-高并发内存池》8. 最终性能优化与测试
c语言·开发语言·数据结构·c++·算法·性能优化
一叶落4387 小时前
LeetCode 74 | 搜索二维矩阵(C语言版题解)
c语言·数据结构·c++·算法·leetcode·矩阵·动态规划