指针的算术运算:加减法与数组遍历
指针的算术运算,说白了就是让指针在内存里「走路」。
我个人习惯把指针想象成一个地址计数器。你给它加1,它就往前走一步;减1,就往后退一步。但这一步跨多大?嗯,这取决于指针的类型。
指针加减法的本质
先看一个最基础的问题:p + 1 到底加了多少?
很多人刚学的时候以为就是地址值加1。其实不是。指针加1,加的是一个完整元素的大小。
举个例子:
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // p 指向 arr[0]
printf("p 的地址: %p\n", p);
printf("p + 1 的地址: %p\n", p + 1);
在我的机器上,输出可能是:
p 的地址: 0x7ffd12345600
p + 1 的地址: 0x7ffd12345604
看到了吗?地址差了4个字节。因为 int 占4字节,所以 p + 1 实际上跳过了4个字节。
核心公式:
指针 + N 的实际地址 = 指针原地址 + N × sizeof(指针指向的类型)
这个规则对减法也一样:p - N 就是往后退 N 个元素。
数组遍历:指针的经典用法
用指针遍历数组,是我在项目中用得最多的场景之一。
我记得有一次写一个网络数据包解析模块,需要逐字节扫描缓冲区。用下标访问当然可以,但用指针更灵活,也更符合底层操作的直觉。
来看一个标准示例:
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p = arr;
int len = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < len; i++) {
printf("arr[%d] = %d\n", i, *(p + i));
}
这里 *(p + i) 等价于 arr[i]。但指针写法更直接地表达了「从起点偏移 i 个位置」这个语义。
你还可以用自增运算符来遍历:
int *p = arr;
int *end = arr + len;
while (p < end) {
printf("%d ", *p);
p++; // 指针向前移动一个 int
}
这种写法在嵌入式开发中很常见。为什么?因为循环条件 p < end 直接比较地址,省去了下标变量,效率更高。
指针相减:计算元素之间的距离
两个指向同一数组的指针可以相减。结果是一个整数,表示它们之间相隔多少个元素。
int arr[] = {10, 20, 30, 40, 50};
int *p1 = &arr[1]; // 指向 20
int *p2 = &arr[4]; // 指向 50
int diff = p2 - p1;
printf("相差 %d 个元素\n", diff); // 输出 3
这里 p2 - p1 的结果是 3,不是 12(字节数)。编译器自动帮你做了除法。
注意:指针相减只有在两个指针指向同一数组(或同一块连续内存)时才有意义。指向不同数组的指针相减,结果是未定义的。
我曾经在调试一个内存池管理代码时,就犯过这个错。两个指针分别指向不同分配块,我拿它们相减想算偏移量,结果跑出来的数值完全不对。查了半天才发现是逻辑问题。
避坑指南:越界的陷阱
指针算术运算最大的坑,就是越界。
我曾经接手过一个老项目,里面有一段代码长这样:
int buf[10];
int *p = buf;
for (int i = 0; i <= 10; i++) {
*p = i * 2;
p++;
}
看出来问题了吗?循环条件是 i <= 10,但数组只有10个元素(下标0到9)。当 i = 10 时,p 已经指向 buf[10]——那是数组之外的内存。
这个 bug 在测试时没暴露,因为 buf[10] 后面恰好是一块空闲内存。但上线后,在某些特定数据量下,程序就会随机崩溃。排查了整整两天才定位到。
我的建议:
- 遍历时始终用
<而不是<=做边界判断 - 养成习惯:指针不要越过数组的最后一个元素
- 如果一定要访问「尾后位置」,只用于比较,不要解引用
一个完整的示例:反转数组
把指针算术运算综合起来,写一个数组反转函数:
void reverse(int *arr, int len) {
int *left = arr;
int *right = arr + len - 1;
while (left < right) {
int temp = *left;
*left = *right;
*right = temp;
left++;
right--;
}
}
这里用了两个指针,一个从头往后走,一个从尾往前走。每次交换它们指向的值。当 left 和 right 相遇或交错时,反转完成。
这种写法比用下标更直观——你一眼就能看出「两个指针在向中间靠拢」。
知识结构图
下面这张图总结了指针算术运算的核心逻辑:
总结一下
指针的加减法,说白了就是「按元素大小步进」。加 N 往前走 N 个元素,减 N 往后退 N 个元素。两个指针相减得到它们之间的元素个数。
用指针遍历数组,比下标更贴近底层,效率也更高。但代价是——你得自己管好边界。越界一次,可能就是一个通宵的调试。
嗯,记住一句话:指针可以指向数组的「尾后位置」,但别解引用它。这是安全底线。
可可精品资料库