指针数组:一群指针的集合
指针数组,说白了就是一个数组,里面装的不是普通整数,而是指针。
我刚开始学的时候,总觉得这玩意儿有点绕。数组就数组,指针就指针,干嘛非要搞个「指针数组」?后来在项目中处理命令行参数时,我才真正体会到它的威力。
指针数组的定义
定义方式很简单:
char *str_arr[5]; // 一个数组,有5个元素,每个元素都是 char* 类型
注意写法。我见过不少新手写成 char (*str_arr)[5],那是数组指针,完全两码事。
来,看个完整的例子:
#include <stdio.h>
int main() {
char *colors[3] = {
"红色",
"绿色",
"蓝色"
};
for (int i = 0; i < 3; i++) {
printf("颜色 %d: %s\n", i, colors[i]);
}
return 0;
}
输出结果:
颜色 0: 红色
颜色 1: 绿色
颜色 2: 蓝色
这里 colors 数组的每个元素,都指向一个字符串常量。你想想看,如果不用指针数组,你要怎么存这三个字符串?用二维数组?那得固定每行的长度,浪费空间。
核心区别:
char *arr[5]→ 指针数组,5个指针char (*arr)[5]→ 数组指针,指向一个包含5个char的数组
指针数组的内存布局
我画了一张图,帮你理解指针数组在内存里长什么样:
看到没?数组里存的是地址,不是字符串本身。每个指针指向内存中某处真正的字符串数据。
指针数组的应用场景
场景一:命令行参数
这是指针数组最经典的应用。你写的每个C程序,main函数其实都长这样:
int main(int argc, char *argv[])
这里的 argv 就是一个指针数组。每个元素指向一个命令行参数字符串。
举个例子:
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("参数个数: %d\n", argc);
for (int i = 0; i < argc; i++) {
printf("参数 %d: %s\n", i, argv[i]);
}
return 0;
}
假设编译成 test,然后运行:
./test hello world 123
输出:
参数个数: 4
参数 0: ./test
参数 1: hello
参数 2: world
参数 3: 123
个人经验:我在做嵌入式调试工具时,经常用命令行参数来控制日志级别。比如 ./monitor -v 3 -o log.txt,用指针数组解析起来特别顺手。
场景二:字符串查找表
我习惯用指针数组做命令映射表。比如:
#include <stdio.h>
#include <string.h>
const char *commands[] = {
"help",
"version",
"status",
"reset"
};
void handle_command(const char *cmd) {
for (int i = 0; i < 4; i++) {
if (strcmp(cmd, commands[i]) == 0) {
printf("执行命令: %s (索引 %d)\n", cmd, i);
return;
}
}
printf("未知命令: %s\n", cmd);
}
int main() {
handle_command("status");
handle_command("reboot");
return 0;
}
输出:
执行命令: status (索引 2)
未知命令: reboot
场景三:多级菜单
嵌入式设备上经常有菜单系统。指针数组用来存菜单项,再合适不过:
const char *main_menu[] = {
"1. 系统设置",
"2. 网络配置",
"3. 诊断测试",
"4. 关于设备"
};
每个菜单项就是一个字符串指针。想改菜单内容?改字符串就行,数组大小都不用动。
我曾经踩过的坑:用指针数组存字符串时,一定要注意字符串的生命周期。如果指向的是局部变量,函数返回后指针就悬空了。我早期一个项目因此出了诡异的内存错误,排查了两天才发现。
指针数组 vs 二维数组
很多人会纠结:存多个字符串,到底用指针数组还是二维数组?
| 特性 | 指针数组 char *arr[] |
二维数组 char arr[][N] |
|---|---|---|
| 内存占用 | 灵活,每个字符串长度可变 | 固定,每行长度相同,浪费空间 |
| 修改字符串 | 可以重新指向新字符串 | 只能修改内容,不能改变长度 |
| 访问速度 | 多一次间接寻址 | 直接访问,稍快 |
| 适用场景 | 字符串长度不固定,需要动态管理 | 字符串长度固定,追求极致性能 |
我个人习惯:除非你明确知道每个字符串长度都一样,否则优先用指针数组。它更灵活,也更符合C语言「信任程序员」的设计哲学。
指针数组的初始化技巧
初始化时,可以用复合字面量(C99起支持):
// 传统方式
const char *days[] = {"Mon", "Tue", "Wed"};
// 动态分配后赋值
char **names = malloc(3 * sizeof(char *));
names[0] = "Alice";
names[1] = "Bob";
names[2] = "Charlie";
动态分配时,记得每个指针可以指向不同长度的字符串。这是二维数组做不到的。
避坑指南:
- 指针数组不检查边界,越界访问是未定义行为
- 如果指针指向动态分配的内存,记得逐个释放
- const修饰指针数组时,注意语义:
const char *arr[]表示字符串内容不可改,但指针本身可改
小结
指针数组,说白了就是「数组里放指针」。它最牛的地方在于:每个元素可以指向不同类型、不同大小的数据。命令行参数、菜单系统、命令查找表……这些场景里,指针数组都是不二之选。
嗯,记住那个定义写法:类型 *数组名[大小]。别跟数组指针搞混了就行。
公众号:蓝海资料掘金营,微信deep3321