<p>指针数组是每个元素为指针的数组,声明需明确类型和大小,如char* names[3] = {"Alice", "Bob", "Charlie"};易错点包括混淆指针数组与数组指针、未初始化致野指针、sizeof误算、忽略生命周期及边界检查。</p>

指针数组的声明和初始化必须明确类型和大小
指针数组本质是「数组」,每个元素都是指针,不是「指向数组的指针」。常见错误是混淆 int* arr[5](指针数组)和 int (*arr)[5](数组指针)。前者能存 5 个 int*,后者是指向含 5 个 int 的数组的单个指针。
声明时必须指定元素数量(除非用 std::vector 动态替代),且类型要与所指向对象严格匹配:
char* names[3] = {"Alice", "Bob", "Charlie"}; // ✅ 合法:3 个 char* 元素
int* nums[2] = {nullptr, new int(42)}; // ✅ 合法:显式初始化,注意内存管理
int arr[2] = {1, 2};
int* ptrs[2] = {&arr[0], &arr[1]}; // ✅ 合法:指向栈上变量的地址容易踩的坑:
- 忘记初始化导致野指针:
int* bad[3];中每个元素值未定义,解引用必崩 - 用
sizeof算错总长度:sizeof(names)返回的是 3 × 指针大小(如 24 字节),不是字符串总长 - 误以为可自动推导大小:
char* xs[] = {...};在函数参数中会退化为char**,丢失长度信息
用指针数组实现多字符串操作最常用也最易出错
因为 C 风格字符串本质是 char*,指针数组天然适合管理字符串集合。但要注意生命周期——若指向局部数组,函数返回后指针即失效:
立即学习“C++免费学习笔记(深入)”;
char* getNames() {
char local[10] = "hello";
char* p = local; // ⚠️ local 栈内存即将销毁
return p; // ❌ 返回悬空指针
}安全做法:
- 指向字面量(只读,生命周期全局):
const char* msgs[] = {"OK", "ERROR", "WARN"}; - 指向堆内存(需手动
delete[]或用智能指针) - 指向静态存储区(如
static char buf[64])
遍历时别漏掉边界检查:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
for (int i = 0; i < 3; ++i) {
if (names[i]) printf("%s\n", names[i]); // 加空指针防护更健壮
}传递指针数组给函数时传什么、怎么写形参
函数无法直接接收「带大小的数组」,所以实际上传的是首元素地址(即 char**),原始大小必须额外传入:
void printStrings(char* strs[], int len) { // ✅ 接收方式:strs 是 char**
for (int i = 0; i < len; ++i) {
if (strs[i]) puts(strs[i]);
}
}
// 调用:
char* list[] = {"a", "b", "c"};
printStrings(list, 3); // ✅ 必须显式传 3不能写成 void f(char* [3]) —— 这种语法在形参里等价于 char**,编译器忽略数字 3;也不能省略 len 参数,否则函数内无法知道数组有多长。
若想保留长度信息,改用 std::array<char n></char> 或 std::vector<char></char> 更安全。
指针数组和二维数组在内存布局上完全不同
这是最容易引发越界或误读的点。假设:
int a[2][3] = {{1,2,3}, {4,5,6}}; // 连续 6 个 int,行优先存储
int* b[2] = {new int[3], new int[3]}; // 2 个指针,各自指向不连续的堆内存块后果:
-
a[0]+1指向a[0][1](同一块内存),b[0]+1指向b[1](下一个指针,不是下一个 int) - 把
b强转成int**再传给期望int (*)[3]的函数,会触发未定义行为 - 释放
b时必须逐个delete[] b[i],不能只delete[] b
真正需要“动态二维数组”时,优先考虑 std::vector<:vector>></:vector>,避免手管指针数组的碎片化内存和释放陷阱。

















