指针自增(p++)或自减(p--)按sizeof(p)字节偏移,而非固定±1;如int加4、char*加1,本质是跳向下一个同类型元素,编译器自动缩放,越界则触发未定义行为。

指针自增(p++)和自减(p--)到底移动多少字节
它不加 1 或减 1 个字节,而是加 / 减 sizeof(*p) 个字节。这是最常被初学者误解的一点。
比如 int *p 指向一个 int 变量,p++ 后,p 的地址值增加 4(在多数平台下),因为 sizeof(int) == 4;而 double *q 执行 q++,地址增加 8。
- 本质是“指向下一个同类型元素”,不是“地址+1”
- 编译器自动根据指针类型做缩放,你不需要手动乘
sizeof - 若指针类型为
char *,则p++确实只加 1 字节——因为sizeof(char) == 1
++p 和 p++ 在指针场景下的行为差异
前缀和后缀形式对指针变量本身的影响一致(都让指针前进一个元素),但表达式的值不同:前者返回移动后的地址,后者返回移动前的地址。
这在赋值或函数调用中会直接影响结果:
立即学习“C++免费学习笔记(深入)”;
-
int *q = p++;→q得到原地址,p已移向下一元素 -
int *q = ++p;→p先移,q得到新地址 -
func(p++);→func收到的是旧地址,p在调用后才变 -
func(++p);→p先变,func收到新地址
哪些操作会导致未定义行为(UB)
指针自增/自减本身合法,但越界或违反对象生命周期时,行为就不可预测——编译器不会报错,运行时可能崩溃或静默出错。
-
int arr[3]; int *p = arr; p += 5;→ 越界,UB(即使没解引用) -
int *p = new int(42); delete p; p++;→ 对已释放内存的指针做算术,UB -
int *p = nullptr; p++;→ 对空指针自增,UB(C++ 标准明确禁止) -
int a, b; int *p = &a; p = &b; p--;→p原不指向连续内存,p--无定义
和数组下标访问 a[i] 的等价关系
*(p + i) 和 p[i] 完全等价,且都要求 p + i 在合法范围内。这不是语法糖,而是 C++/C 标准明确定义的语义。
-
p[0]⇔*(p + 0)⇔*p -
p[3]⇔*(p + 3),前提是p + 3指向同一数组内或紧邻末尾的合法位置 -
&p[i]⇔p + i,二者地址相同,类型也一致(int *) - 但
p[-1]合法的前提是p本身指向某数组第 2 个或之后元素(即p - 1仍在对象边界内)
真正容易被忽略的是:指针算术的合法性不取决于你是否解引用,而取决于地址计算本身是否落在单一对象(或其末尾一单位)内。哪怕只是写 p + 100,只要超出了对象边界,就是 UB。


















