C 语言 - 指针算术运算
指针算术运算
指针算术运算指的是改变指针的值,使其指向内存中的不同元素。
正如我们在上一页所见,数组元素在内存中是相邻存储的。因此,如果一个指针指向一个元素,加 1 就会使其移动到下一个元素:
实例
int myNumbers[4] = {25, 50, 75, 100};
int *p = myNumbers; // 指向 myNumbers[0]
printf("%d\n", *p); // 25
printf("%d\n", *(p + 1)); // 50
printf("%d\n", *(p + 2)); // 75
printf("%d\n", *(p + 3)); // 100
自增和自减
您可以使用 ++ 和 --(以及 += / -=)来移动指针:
实例
int myNumbers[3] = {10, 20, 30};
int *p = myNumbers; // 指向 myNumbers[0]
printf("%d\n", *p); // 10
p++; // 移动到 myNumbers[1]
printf("%d\n", *p); // 20
p--; // 移回 myNumbers[0]
printf("%d\n", *p); // 10
p += 2; // 跳转到 myNumbers[2]
printf("%d\n", *p); // 30
这表明您可以像操作计数器一样移动指针:每次使用 p++ 或 p--,它就会移动到数组中的下一个或上一个元素。
指针相减(距离)
您可以减去两个指向同一数组中元素的指针,以找出它们之间相隔多少个元素:
实例
int myNumbers[5] = {10, 20, 30, 40, 50};
int *start = &myNumbers[1]; // 指向 20
int *end = &myNumbers[4]; // 指向 50
printf("%ld\n", end - start); // 相隔 3 个元素
在这里:
start指向第二个元素(20)。end指向第五个元素(50)。end-start给出它们之间的元素个数:3。
注意:指针相减仅当两个指针指向同一个数组时才有效。结果以元素个数为单位,而不是字节。
指针算术运算取决于类型
并非所有指针的移动方式都相同。
当您给指针加 1 时,它向前移动的距离是其指向类型的大小——而不仅仅是 1 个字节。
例如:
- 一个
int*指针移动的距离是一个整数的大小(通常是 4 字节)。 - 一个
char*指针移动的距离是一个字符的大小(1 字节)。
因此,如果两个指针都从内存地址 1000 开始:
int*→p + 1将移动到地址 1004char*→p + 1将移动到地址 1001
这表明指针的移动取决于它指向的数据类型——而不是您加的数字:
实例
int myNumbers[2] = {1, 2};
char letters[] = "Hi"; // 'H', 'i', '\0'
int *pi = myNumbers; // int 指针
char *pc = letters; // char 指针
printf("%p\n", (void*)pi);
printf("%p\n", (void*)(pi + 1)); // 移动 sizeof(int) (4 字节)
printf("%p\n", (void*)(pi + 2)); // 移动 sizeof(int) (4 字节)
printf("%p\n", (void*)pc);
printf("%p\n", (void*)(pc + 1)); // 移动 1 字节
使用指针循环
在上一章中,您学习了如何使用 *(ptr + i) 遍历数组。
现在让我们看看另一种方法——通过在循环内部移动指针本身。每次指针增加(p++),它就会移动到内存中的下一个元素:
实例
int myNumbers[4] = {25, 50, 75, 100};
int *p = myNumbers; // 数组起始位置
for (int i = 0; i < 4; i++) {
printf("%d\n", *p);
p++; // 移动到下一个元素
}
这是每个循环中发生的事情:
*p给出当前元素的值。p++将指针移动到数组中的下一个元素。- 不需要数组索引 (
i)——指针自己跟踪位置。
提示:这种循环方式在直接处理内存时很常见,因为指针本身在数组中移动,而不是使用索引号。
需要避免的常见错误
- 使用错误的类型:请记住指针移动取决于其类型。
int*以 4 字节步长移动,但char*一次移动 1 字节。混淆它们将会指向错误的内存位置。 - 未初始化的指针:在使用指针之前,始终确保它指向某个真实的东西。使用不指向任何地方的指针可能会使程序崩溃。
- 越界:切勿将指针移动到数组末尾之后或起始之前。唯一安全的"外部"位置是数组末尾再过去一步,并且这仅用于比较指针——而不是访问值。
并且要小心;必须谨慎处理指针,因为它有可能损坏存储在其他内存地址中的数据。