指针数组是数组,每个元素为指针类型,定义为int arr[5],方括号须紧跟变量名;易与数组指针int (arr)[5]混淆;初始化需用有效地址或nullptr,推荐std::array/std::vector替代。

指针数组的定义语法别写成数组指针
很多人一上来就写 int* arr[5],这没错;但容易和 int (*arr)[5] 混淆——后者是「指向含 5 个 int 的数组的指针」,完全不是一回事。指针数组本质是「数组」,每个元素都是指针类型,所以方括号必须紧跟在变量名后面。
常见错误现象:int* p[3] = {1, 2, 3} 编译失败,因为 1、2、3 是整数,不能直接赋给 int* 类型元素。
-
int* arr[4]:定义一个长度为 4 的数组,每个元素都是int* - 声明时未初始化,所有指针值是未定义的(不是
nullptr) - 若想初始化为安全空值,显式写
int* arr[4] = {nullptr, nullptr, nullptr, nullptr}或简写为int* arr[4] = {}(C++11 起支持聚合初始化为零)
初始化时注意地址有效性与生命周期
用取地址符 & 初始化,必须确保被取地址的变量在指针数组整个使用期间都有效。局部变量的地址存进指针数组后,函数返回就悬空了。
典型错误场景:int* ptrs[2]; { int x = 10; ptrs[0] = &x; } —— x 作用域结束,ptrs[0] 成为悬垂指针。
- 安全做法:指向全局变量、静态变量、堆内存(
new出来的)、或传入的长期有效的实参 - 示例:
int a = 1, b = 2; int* arr[2] = {&a, &b}; - 动态分配:
int* arr[2] = {new int(10), new int(20)};,但记得后续delete,否则泄漏 - 字符串字面量可直接用:
const char* strs[2] = {"hello", "world"};,因为字面量存储在只读数据段,生命周期是整个程序运行期
C++11 及以后推荐用 std::array 或 std::vector 替代原生指针数组
原生指针数组不带长度信息,无法安全遍历,也不自动管理内存,容易越界或忘记释放。现代 C++ 更倾向容器封装。
性能影响几乎为零:std::array<int></int> 是零开销抽象,编译后和原生数组一样;std::vector<int></int> 多一次堆分配,但换来安全性与灵活性。
-
std::array<int> arr = {&x, &y, &z};</int>—— 长度固定,栈上分配,支持.size()、范围 for -
std::vector<int> vec; vec.push_back(&x);</int>—— 动态增删,自动扩容,避免手动算下标 - 如果指针指向堆对象,考虑用
std::unique_ptr管理所有权:std::array<:unique_ptr>, 3> smart_arr;</:unique_ptr>
访问指针数组元素时解引用要分两步走
别把 arr[i] 当成值来用,它只是一个指针。要拿到它指向的内容,必须再加一次 *。漏掉第二层解引用是低级但高频的 bug。
错误写法:cout 输出的是地址(如 <code>0x7ffd1234abcd),不是值;正确是 cout
-
arr[i]:取第 i 个指针(类型是int*) -
*arr[i]:取该指针所指的值(类型是int) - 若指针为
nullptr,*arr[i]行为未定义,务必检查:if (arr[i]) { cout - 多级间接也常见:
int** pp = &arr[0]; cout —— 先解 <code>pp得到arr[0],再解得到值
原生指针数组本身很简单,难的是管住它的生命周期、避免悬垂、防止越界,还有别在解引用层级上犯迷糊。这些地方出问题,调试起来往往不报错,只是结果随机。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











