最近跟着教程手写c语言指针知识的过程,恰好碰到了这个二维数组指针也就是所谓的:
数组指针和指针数组的关系如何分辨的这个难题
恰好今天有时间来写一篇帖子专门来讲讲:
一、指针数组:字符串军火库
刚学 C 语言时,总被char *arr[] = {"小鸡", "牛逼", "不在这里"}
这种代码搞懵:
这货明明是数组,为啥每个元素都是字符串?还能直接用双引号初始化?
别急,先看个生活场景:
你有一排书架,每个书架上挂着一个标签,标签写着 "武侠区"" 科幻区 ""禁书区"——
指针数组就是这个标签数组,每个标签(指针)指向书架里的具体内容(字符串)。
代码背后的真相:
char* arr[] = {"小鸡", "牛逼", "不在这里"};
- 本质:这是一个数组,每个元素是
char*
类型的指针 - 初始化魔法:
双引号字符串在 C 语言中会被自动转化为char*
指针(指向字符串首字符)
相当于:char* arr[3]; arr[0] = "小鸡"; // "小鸡"是隐藏的char数组,此处取其首地址 arr[1] = "牛逼"; arr[2] = "不在这里";
- 内存布局:
- 字符串 "小鸡"" 牛逼 " 等存放在只读数据段(程序运行时不能修改)
- 指针数组
arr
存放在栈区,每个元素存的是对应字符串的地址
二、指针数组 vs 数组指针:傻傻分不清楚?
这是新手最容易踩的坑,用快递柜举例:
-
指针数组:
char* arr[5]
✅ 一排 5 个快递柜,每个柜子里放的是快递单号(指针),指向不同的快递(字符串)
特点:每个 "单号" 可以指向不同长度的快递(字符串长度随意) -
数组指针:
char (*arr)[5]
✅ 一个能打开 5 格大柜子的钥匙,专门用来开长度固定为 5 的字符数组
特点:必须指定柜子大小(数组长度),否则钥匙无效
看代码:
// 指针数组:3个字符串指针
char* str_arr[] = {"a", "bb", "ccc"};
// 数组指针:指向一个包含5个字符的数组
char arr[5] = "hello";
char (*ptr_arr)[5] = &arr; // 必须带&,且数组长度必须匹配!
三、指针数组的 "逆天" 应用
1. 命令行参数:程序的 "启动钥匙串"
每个 C 语言程序的main
函数都有隐藏参数:
int main(int argc, char* argv[]) {
// argv就是一个指针数组!
// argc是参数个数,argv[0]是程序名,argv[1]~argv[n]是参数
}
🌰 实战演示:
当你在终端输入:bash
./program 小鸡 牛逼 "不在这里"
argv
数组会变成:
argv[0] = "./program" // 程序路径
argv[1] = "小鸡"
argv[2] = "牛逼"
argv[3] = "不在这里"
面试高频问题:
为什么argv
是char*[]
而不是char[][]
?
💡 答案:因为每个参数长度不同,用指针数组更灵活(二维数组需要固定列数)
2. 动态管理字符串:灵活到飞起
普通二维数组存字符串:
char bad_arr[100][20]; // 强制每个字符串最多19字符,浪费内存!
指针数组存字符串:
char* good_arr[] = {"短", "超级长超级长超级长的字符串", " medium"};
// 每个字符串独立存储,想多长多长!
四、面试必问!指针数组的 "死亡陷阱"
📌 陷阱 1:修改字符串字面量
char* arr[] = {"小鸡"};
arr[0][0] = '大'; // ❌ 危险!会崩溃!
💥 原因:
"小鸡" 存放在只读数据段,试图修改会触发段错误(Segmentation Fault)
正确操作:如果需要修改,先拷贝到可写内存:
char buf[20];
strcpy(buf, arr[0]); // 拷贝到栈区数组
buf[0] = '大'; // ✅ 安全修改
📌 陷阱 2:指针数组长度计算
char* arr[] = {"a", "bb", "ccc"};
int len = sizeof(arr); // ❌ 错误!sizeof(arr)是整个数组的字节数
int correct_len = sizeof(arr) / sizeof(arr[0]); // ✅ 正确!得到元素个数3
📌 陷阱 3:和二维数组的终极 battle
特性 | 指针数组 | 二维字符数组 |
---|---|---|
内存占用 | 小(仅存指针) | 大(存所有字符串) |
字符串长度限制 | 无 | 必须固定(列数决定) |
修改字符串可行性 | 只能改指针指向 | 可直接修改内容 |
典型场景 | 命令行参数、字符串列表 | 固定长度字符串存储 |
五、实战演练:用指针数组玩出花
案例:实现一个简单的 "字符串字典"
#include <stdio.h>
// 定义一个指针数组,存储常用问候语
char* greetings[] = {
"你好", "哈喽", "Ni Hao", "Bonjour",
"Hello", "こんにちは", "안녕하세요"
};
int main() {
printf("今天想学哪种语言的问候?\n");
for (int i = 0; i < sizeof(greetings)/sizeof(greetings[0]); i++) {
printf("%d. %s\n", i+1, greetings[i]);
}
return 0;
}
运行结果:
今天想学哪种语言的问候?
1. 你好
2. 哈喽
3. Ni Hao
4. Bonjour
5. Hello
6. こんにちは
7. 안녕하세요
六、总结-指针数组三板斧
- 本质:存储指针的数组,每个指针指向一个字符串(或其他数据)
- 核心优势:灵活管理不同长度的字符串,节省内存
- 避坑:
- 别碰字符串字面量(只读的)
- 计算长度用
sizeof(arr)/sizeof(arr[0])
- 区分指针数组和数组指针(看有没有括号!)
最后送大家在博客上看到的一句:
指针数组像书架,字符串标签墙上挂,类型区别要记牢
如果觉得有用,麻烦给我点赞收藏关注转发!感谢啦 !