一、基础概念
指针 p 存放地址 ,*p 取地址对应的值;
++/-- 分前置 (++p、--p)、后置 (p++、p--);
指针自增不是地址+1,而是向后偏移一个指向类型的字节大小。
示例:int *p,int占4字节,则 p++ 等价于 p = p + 4。
二、四种写法核心区别
1. 后置自增 p++
- 先使用原始p的值参与运算;
- 运算结束后,指针再向后偏移1个类型单位。
c
int arr[] = {10,20,30};
int *p = arr;
int val = *p++;
// 拆解:先取 *p = 10 赋值给val,再 p = p + 4
// val=10,p现在指向20
2. 后置自减 p--
- 先使用原始p的值参与运算;
- 运算结束后,指针向前偏移1个类型单位。
c
int val = *p--;
// 先取当前*p,再p往前移一位
3. 前置自增 ++p
- 指针先偏移,再使用新地址参与运算。
c
int arr[] = {10,20,30};
int *p = arr;
int val = *++p;
// 拆解:先 p=p+4 指向20,再取值 *p=20 赋给val
// val=20
4. 前置自减 --p
- 指针先向前偏移,再取值/运算。
c
int val = *--p;
// p先前移一位,再取新地址的值
三、优先级对比(关键)
++/-- 后置 > * 解引用 > ++/-- 前置
等价括号写法:
*p++→*(p++):先取值,后移指针*++p→*(++p):先移指针,后取值(*p)++:取p指向的值,值自增,指针不变(极易混淆!)++(*p):p指向的值先自增,指针不变
易错示例区分
c
int a = 5;
int *p = &a;
// 1. *p++
int x = *p++;
// x=5,p地址+4,a不变
// 2. (*p)++
int y = (*p)++;
// y=5,a变成6,p地址不变
// 3. ++(*p)
int z = ++(*p);
// a先+1=7,z=7,p不变
四、指针自增 vs 自减 行为对比表
| 表达式 | 执行顺序 | 指针变化 | 典型用途 |
|---|---|---|---|
p++ |
先用原地址,后向后移 | 地址+sizeof(类型) | 顺序遍历数组,读完当前再走下一个 |
++p |
先向后移,再用新地址 | 地址+sizeof(类型) | 跳过首元素,从第二个开始遍历 |
p-- |
先用原地址,后向前移 | 地址-sizeof(类型) | 倒序遍历,读完当前看上一个 |
--p |
先向前移,再用新地址 | 地址-sizeof(类型) | 跳过末尾元素,倒序从倒数第二个开始 |
五、完整代码演示对比
c
#include <stdio.h>
int main() {
int arr[4] = {1,2,3,4};
int *p = &arr[1]; // p初始指向2
// 1. *p++
int v1 = *p++;
printf("*p++: val=%d, p指向=%d\n", v1, *p); // val=2, p→3
p = &arr[1]; // 重置
// 2. *++p
int v2 = *++p;
printf("*++p: val=%d, p指向=%d\n", v2, *p); // val=3, p→3
p = &arr[1];
// 3. *p--
int v3 = *p--;
printf("*p--: val=%d, p指向=%d\n", v3, *p); // val=2, p→1
p = &arr[1];
// 4. *--p
int v4 = *--p;
printf("*--p: val=%d, p指向=%d\n", v4, *p); // val=1, p→1
return 0;
}
输出:
*p++: val=2, p指向=3
*++p: val=3, p指向=3
*p--: val=2, p指向=1
*--p: val=1, p指向=1
六、核心总结
- 后置
p++/p--:先使用当前指针地址,运算完再移动指针; - 前置
++p/--p:先移动指针,再使用新地址; (*p)++是值自增,指针地址完全不变,和指针移动无关;- 自增向后走数组、自减向前走数组,遍历时根据是否要跳过当前元素选择前置/后置。