指针加偏移访问数组元素最直接,arr[i] 等价于 *(arr + i),是 C++ 标准定义而非语法糖;需注意类型匹配、边界检查及 const 正确性。

用指针加偏移访问数组元素最直接
数组名本身在多数上下文中退化为指向首元素的指针,所以 arr[i] 等价于 *(arr + i)。这不是语法糖,而是 C++ 标准定义的行为。
常见错误是混淆 arr + i 和 &arr[i]:二者值相同,但类型不同——前者是 T*,后者是 T*(没错,类型一样),但若 arr 是二维数组如 int arr[3][4],arr + 1 类型是 int(*)[4],而 &arr[1][0] 是 int*,混用会导致编译失败或越界读写。
- 确保指针类型与元素类型匹配:
int* p = arr;正确,char* p = arr;会引发未定义行为 - 偏移量
i必须在[0, N)范围内,否则行为未定义(不一定会崩溃,但结果不可靠) - 对
const数组,要用const int* p = arr;,否则编译报错
用指针遍历数组时注意边界和递增方式
用 for (int* p = arr; p != arr + N; ++p) 是安全且惯用的写法;用 p 容易因符号扩展或溢出出错。
常见错误是写成 for (int* p = arr; p ——表面上没问题,但若 <code>N 是无符号大数(比如 size_t),而 p 是有符号指针类型(如 int*),比较可能被隐式转换,导致逻辑异常。
立即学习“C++免费学习笔记(深入)”;
- 优先用
!=而非做终止判断,尤其在模板或泛型代码中 -
++p和p++在循环中效果一致,但前者略高效(避免临时对象),习惯上用前置 - 若数组长度未知(如传入函数的
int arr[]),必须额外传入长度,指针本身不携带大小信息
函数参数里传数组本质上传的是指针
声明 void func(int arr[10]) 和 void func(int* arr) 完全等价,编译器一律按指针处理。方括号里的数字被忽略,仅作文档提示。
因此,sizeof(arr) 在函数体内永远返回指针大小(通常是 8),而非数组总字节数——这是新手最常踩的坑。
- 想保留长度信息,改用引用:
void func(int (&arr)[10]),此时sizeof(arr)正确返回40(假设int为 4 字节) - 现代 C++ 更推荐
std::array<int></int>或std::span<int></int>(C++20),它们自带尺寸且更安全 - 如果坚持用裸指针传参,务必约定调用方提供长度参数,例如
func(arr, 10)
多维数组指针访问容易搞错类型和步长
对于 int mat[3][4],mat 类型是 int[4] 的数组,退化为 int(*)[4](指向含 4 个 int 的数组的指针),不是 int**。
误写 int** p = mat; 会编译失败;强行 int** p = reinterpret_cast<int>(mat);</int> 后再解引用,几乎必然越界——因为 mat + 1 实际跳过 4 个 int,而 *p 会当作单个 int* 去读,破坏内存布局。
- 正确访问第
i行第j列:(*mat)[j](首行)、(* (mat + i))[j]或更清晰地写成mat[i][j] - 若需统一用指针算术遍历全部元素,用
int* p = &mat[0][0]; p[i * 4 + j] - 动态二维数组(如
int** mat)才是真正的指针数组,其内存不连续,访问方式完全不同
指针访问数组看似简单,但类型、边界、多维布局这三处稍不留神就会引入静默错误——尤其是当代码从一维扩展到二维,或从栈数组换成堆分配时,指针语义不会自动适配。


















