
怎么声明和初始化一个指针数组
指针数组本质是「数组」,每个元素都是指针。它不是某种高级技巧,就是普通数组的类型换成了指针类型。
声明时别被语法绕晕:int<em> arr[5]</em> 是指针数组(5个 int 元素),而 int (*arr)[5] 是数组指针(指向含5个 int 的数组)——光看括号位置就决定语义。
常见错误:把 int<em> arr[5]</em> 写成 int arr[5] 就以为是“指针的数组”,其实空格不影响解析,但写成 int<em> arr[5]</em> 更直观体现“int 类型的数组”。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 初始化时可直接用地址:
int a = 1, b = 2; int* arr[2] = {&a, &b}; - 若想存字符串字面量,用
const char*类型更安全:const char* names[] = {"Alice", "Bob"}; - 避免用未初始化的指针元素,比如
int* arr[3]; printf("%d", *arr[0]);会崩溃
数组指针怎么用、什么时候必须用它
数组指针是指向「整个数组」的指针,典型场景是传参时保持维度信息,或操作二维数组的行。
比如函数要接收一个 int[3][4] 的二维数组,形参不能写 int arr[3][4](退化为 int (*)[4]),也不能写 int*<em> arr</em>(类型不匹配,且丢失列数)。正确写法是 void func(int (arr)[4], int rows)。
常见错误现象:
- 把二维数组名直接传给
int**参数,编译可能过,运行时解引用错位 - 写
int (*p)[5] = new int[10][5];合法;但写int (*p)[5] = new int[5];编译失败——右边不是「数组类型」
性能上,数组指针本身只是个地址,开销和普通指针一样;但它能帮编译器做边界计算(如 p+1 跳过整行),比用 int* 手动算偏移更可靠。
为什么 sizeof 对两者结果差这么多
这是最直观的区分方式,也是调试时快速判断类型的关键。
对指针数组 int<em> arr[10]</em>:sizeof(arr) 返回 10 sizeof(int*)(比如 80 字节 on x64),因为它是数组。
对数组指针 int (<em>p)[10]</em>:sizeof(p) 就是 sizeof(int)(通常 8 字节),因为它只是一个指针变量。
容易踩的坑:
- 在函数内对形参用
sizeof判断长度——无论原类型是什么,只要作为参数传入,数组都会退化为指针,sizeof失效 - 误以为
int* arr[5]和int** p可互换,但前者是固定大小的栈数组,后者是单个指针,sizeof结果完全不同
动态分配指针数组和数组指针的实际写法
动态分配不是靠猜,得按类型来。
分配指针数组(比如存 10 个 int*):
int** arr = new int*[10]; // 注意:是 int**, 不是 int*
for (int i = 0; i < 10; ++i) {
arr[i] = new int(42);
}
// 释放要两层 delete
for (int i = 0; i < 10; ++i) delete arr[i];
delete[] arr;
分配数组指针所指的对象(比如一个 3×4 的二维数组):
int (*p)[4] = new int[3][4]; // p 指向含 4 个 int 的数组 p[0][0] = 1; delete[] p; // 注意:只用一次 delete[]
关键区别在于:指针数组的「数组部分」是动态分配的指针容器;数组指针的「所指对象」才是真正的多维内存块。混淆这两者,new/delete 配对就会出错,轻则内存泄漏,重则崩溃。
类型擦除、隐式转换、模板推导时,这两者的差异会被放大。写清楚 * 和 [ ] 的绑定关系,比背口诀管用得多。










