指针加减操作按所指类型大小自动缩放,而非简单地址数值增减;int加1实际偏移4字节,char加1偏移1字节,误判会导致越界或跳过元素。

指针加减到底在加减什么
指针算术不是简单地对地址数值做加减,而是按所指类型的大小自动缩放。比如 int* 指针加 1,实际地址偏移是 sizeof(int) 字节(通常是 4),不是 1 字节。
- 错误现象:
char* p = &arr[0]; p + 1移动 1 字节,但int* p = &arr[0]; p + 1移动 4 字节 —— 若误以为都是+1字节,遍历时会越界或跳过元素 - 使用场景:遍历数组、实现 memcpy / memmove 的底层循环、写 ring buffer 索引计算
- 注意
void*不支持算术运算(C++11 起编译报错),必须先转为具体类型指针
用 ptr[i] 还是 *(ptr + i)
二者完全等价,编译器生成的汇编指令一致,选哪个纯看可读性。但理解后者有助于看清底层机制。
-
arr[i]是语法糖,本质就是*(arr + i);arr本身是数组名,隐式退化为指针 - 性能无差异,现代编译器都优化成相同指令(如 x86 的
lea或直接基址+索引寻址) - 容易踩坑:
ptr[-1]合法但危险,需确保ptr不是指向数组首元素的指针(否则越界未定义)
跨数组边界的指针算术是未定义行为
C++ 标准只允许指针在“同一数组对象内”或“刚好指向末尾后一位置”做算术。超出即未定义,哪怕地址看起来合法。
- 常见错误:
int arr[5]; int* p = &arr[4]; p + 2;——p + 1指向&arr[5](允许),但p + 2已越界(不允许) - 即使
arr后面紧挨着另一数组,也不能靠指针算术跨过去访问 —— 编译器可能做激进优化(如假设不会越界而删掉边界检查) - 调试时可能“恰好工作”,但换编译器/优化等级就崩溃,尤其
-O2下更易暴露
与 std::vector 和 std::array 的兼容性
只要容器内存连续(std::vector 和 std::array 都保证),就能安全用指针算术遍历,但要注意获取方式。
立即学习“C++免费学习笔记(深入)”;
-
std::vector:用&vec[0]或vec.data()得到T*,再做算术;空 vector 时data()可能返回 nullptr,需判空 -
std::array:同理,arr.data()或&arr[0]都行,但arr.begin()返回的是迭代器,不是原生指针(除非显式取地址) - 别直接对
std::vector::iterator做算术(虽然通常可行),因它只是类模板实例,标准不保证其底层是裸指针 —— 用std::distance或转data()更稳妥
最常被忽略的一点:指针算术依赖 sizeof(T),而结构体有对齐填充。若自己手写内存池或序列化逻辑,光数字段字节数不够,得用 sizeof 实测,不然偏移算错。










