指针遍历数组时arr自动退化为int*,可直接赋值给指针变量并递增访问;常见错误是循环条件未限制p < arr+5导致越界。

用指针访问数组首地址后递增遍历
数组名在大多数上下文中会自动退化为指向首元素的指针,所以 int arr[5] 的 arr 本身就可以当 int* 用。遍历时只需用指针变量接收它,再通过 ++ 或 += 移动位置。
常见错误是越界:比如写 for (int* p = arr; p ,<code>arr + 5 已经是末尾元素的下一个地址(合法但不可读),解引用它会触发未定义行为。
- 正确边界判断用
p != arr + n或p (<code>n是元素个数) - 避免混用
sizeof(arr)计算长度——若arr是函数参数,sizeof返回指针大小而非数组总字节数 - 推荐用
std::size(arr)(C++17 起)或模板推导获取长度,但仅适用于原生数组且非形参
用指针偏移语法 ptr[i] 替代 *(ptr + i)
ptr[i] 和 *(ptr + i) 完全等价,编译器生成的汇编通常一致。选 ptr[i] 更符合阅读直觉,尤其在多层嵌套或条件索引时。
注意:这不意味着你在“用数组方式访问”,底层仍是纯指针运算。例如 int* p = arr + 2; 后,p[0] 是第三个元素,p[1] 是第四个——下标始终相对于当前指针位置。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 不要误以为
p[i]会自动绑定到原始数组起始位置 - 若需固定基址访问,显式写成
arr[i]或保存原始指针副本 - 性能无差异,但
ptr[i]在复杂表达式中可读性更高(如(p + offset)[i])
传入函数后如何安全遍历(避免退化陷阱)
函数参数写 void func(int arr[]) 或 void func(int* arr),实际都是接收指针,sizeof(arr) 永远是 sizeof(int*)。此时必须额外传长度。
典型错误是试图在函数内用 sizeof(arr)/sizeof(arr[0]) 算长度,结果得到 8/4=2(64 位系统),导致遍历崩溃。
- 强制要求调用方传入长度:
void func(int* arr, size_t n) - 用模板推导(适用于编译期已知大小的数组):
template<size_t n> void func(int (&arr)[N]) { /* sizeof(arr) 正确 */ }</size_t> - C++20 可考虑
std::span<int></int>,自带长度信息且不拷贝数据
为什么不用 std::vector?什么场景还必须用原生指针
除非对接 C API、嵌入式裸机、高性能计算内核或已有大量 C 风格接口,否则优先用 std::vector。它的迭代器本质就是带边界检查(Debug 模式)和容量管理的指针封装。
原生指针遍历的不可替代场景:需要精确控制内存布局(如 GPU 显存映射)、规避任何 STL 运行时开销、或实现底层容器(如 vector 自身的迭代器)。
-
std::vector::data()返回的就是T*,可直接用于传统指针遍历 - 调试时打印
&arr[0]和arr.data()地址相同,说明底层连续 - 真正难的是生命周期管理——指针不管理内存,忘了
delete[]或重复释放,问题比容器更隐蔽
C++ 指针遍历本身很简单,难点全在边界控制、生命周期归属和上下文语义理解上。尤其是传参后数组退化这个点,几乎每个 C++ 初学者都踩过坑。

















