
摘要
在学习 C 语言指针时,很多同学会卡在"指针数组""二级指针"这些概念上,感觉代码能跑,但就是不知道有什么用。
本文通过一个学生成绩管理的小功能,把"指针数组""指向指针的指针"真正用起来,结合完整代码,逐行解释指针的变化过程,让你明白:
- 指针数组到底解决了什么问题
- 为什么要用"指向指针的指针"
- 这种写法在真实程序中能做什么
如果你已经学过数组和一级指针,这篇文章可以帮你把知识真正串起来。
描述(实际使用场景)
假设我们在做一个学生成绩管理程序:
- 学校里有多个学生
- 每个学生的成绩单独存放
- 程序需要统一管理这些成绩数据,并依次输出
如果我们直接用二维数组,当然也能做,但灵活性不够。
真实项目里,不同学生的数据往往来自不同位置 ,这时候就非常适合用指针数组来管理。
于是我们可以这样设计:
- 用一个整型数组
a保存某个学生的成绩 - 用一个指针数组
num,每个元素都指向a中的某一个成绩 - 再用一个指向指针的指针
p,统一遍历这些指针
这和你给出的教材示例在逻辑上是完全一致的,只是换成了一个"能说清用途"的场景。
题解答案(思路说明)
实现思路可以分成四步:
- 定义一个整型数组
a,保存学生的成绩 - 定义一个指针数组
num,让它指向a中的每个元素 - 定义一个指向指针的指针
p,指向num的首元素 - 通过移动
p,间接访问并输出每一个成绩
这个过程看起来绕,但本质是:
用指针数组统一管理数据地址,再用二级指针进行遍历。
题解代码分析
完整代码
c
#include <stdio.h>
int main()
{
// 学生成绩数组
int a[5] = {1, 3, 5, 7, 9};
// 指针数组,每个元素指向成绩数组中的一个元素
int *num[5] = { &a[0], &a[1], &a[2], &a[3], &a[4] };
// 指向指针的指针
int **p;
int i;
// 让 p 指向指针数组 num 的首元素
p = num;
// 遍历并输出成绩
for (i = 0; i < 5; i++)
{
printf("%d ", **p);
p++;
}
printf("\n");
return 0;
}
关键代码逐行讲解
成绩数组
c
int a[5] = {1, 3, 5, 7, 9};
这一步很简单,相当于存了 5 个学生成绩。
指针数组的作用
c
int *num[5] = { &a[0], &a[1], &a[2], &a[3], &a[4] };
这里是重点。
num是一个数组- 数组里的每个元素都是
int * - 每个指针都指向成绩数组
a的一个元素
你可以把它理解成:
num 不是存成绩,而是存"成绩的地址"。
指向指针的指针
c
int **p;
这表示:
p指向的是一个int *- 也就是说,
p指向的是num里的某一个元素
p 的初始化
c
p = num;
这一步非常关键:
-
num代表指针数组首元素的地址 -
p开始指向num[0] -
此时:
*p等价于num[0]**p等价于a[0]
输出逻辑
c
printf("%d ", **p);
这是整个程序最容易让人迷糊的地方:
p指向num[i]*p得到&a[i]**p得到a[i]的值
指针移动
c
p++;
每次 p++:
- 指向下一个
num元素 - 间接访问下一个成绩
示例测试及结果
程序运行输出
1 3 5 7 9
对应关系说明
| p 指向 | *p 的值 | **p 的值 |
|---|---|---|
| num[0] | &a[0] | 1 |
| num[1] | &a[1] | 3 |
| num[2] | &a[2] | 5 |
| num[3] | &a[3] | 7 |
| num[4] | &a[4] | 9 |
这样一对照,二级指针的逻辑就非常清楚了。
时间复杂度
- 程序中只有一次
for循环 - 循环次数为
n(这里是 5)
时间复杂度为:
O(n)
空间复杂度
- 成绩数组
a占用n个整型空间 - 指针数组
num占用n个指针空间 - 额外只用了一个二级指针
p
空间复杂度为:
O(n)
总结
这段代码真正想教你的不是"怎么多写几个星号",而是:
-
指针数组适合用来管理多个分散的数据地址
-
二级指针非常适合统一遍历指针数组
-
这种写法在真实项目中很常见,比如:
- 命令行参数
argv - 多个字符串的统一管理
- 多个数据块的集中处理
- 命令行参数
如果你现在回头再看教材里的示例,会发现它不再是"为了考试而存在",而是一个能直接迁移到真实项目里的思想。