C 指针的算术运算

C 指针 C 指针

C 指针是一个用数值表示的地址。因此,您可以对指针执行算术运算。可以对指针进行四种算术运算:++、--、+、-。

假设 ptr 是一个指向地址 1000 的整型指针,是一个 32 位的整数,让我们对该指针执行下列的算术运算:

ptr++

在执行完上述的运算之后,ptr 将指向位置 1004,因为 ptr 每增加一次,它都将指向下一个整数位置,即当前位置往后移 4 字节。这个运算会在不影响内存位置中实际值的情况下,移动指针到下一个内存位置。如果 ptr 指向一个地址为 1000 的字符,上面的运算会导致指针指向位置 1001,因为下一个字符位置是在 1001。

我们概括一下:

  • 指针的每一次递增,它其实会指向下一个元素的存储单元。
  • 指针的每一次递减,它都会指向前一个元素的存储单元。
  • 指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。

递增一个指针

递增一个指针意味着让指针指向下一个内存位置。

指针的递增操作会根据指针所指向的数据类型进行适当的内存偏移。

我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,数组可以看成一个指针常量。下面的程序递增变量指针,以便顺序访问数组中的每一个元素:

实例

#include <stdio.h> const int MAX = 3; int main () { // 定义一个整数数组 int var[] = {10, 100, 200}; // 定义一个整数变量 i 和一个整数指针 ptr int i, *ptr; // 将指针 ptr 指向数组 var 的起始地址 ptr = var; // 循环遍历数组 for ( i = 0; i < MAX; i++) { // 打印当前指针 ptr 所指向的地址 printf("存储地址:var[%d] = %p\n", i, ptr ); // 打印当前指针 ptr 所指向地址的值 printf("存储值:var[%d] = %d\n", i, *ptr ); // 将指针 ptr 移动到下一个数组元素的位置 ptr++; } return 0; }

当上面的代码被编译和执行时,它会产生下列结果:

存储地址:var[0] = e4a298cc
存储值:var[0] = 10
存储地址:var[1] = e4a298d0
存储值:var[1] = 100
存储地址:var[2] = e4a298d4
存储值:var[2] = 200

递增字符指针:

实例

#include <stdio.h>

int main() {
    char str[] = "Hello";
    char *ptr = str;  // 指针指向字符串的第一个字符

    printf("初始字符: %c\n", *ptr);  // 输出 H

    ptr++;  // 递增指针,使其指向下一个字符
    printf("递增后字符: %c\n", *ptr);  // 输出 e

    return 0;
}

在这个示例中,ptr++ 使指针从 str[0] 指向 str[1]。因为 ptr 是一个 char 类型指针,所以它递增时会移动 sizeof(char) 个字节,即 1 个字节。

当上面的代码被编译和执行时,它会产生下列结果:

初始字符: H
递增后字符: e

递增结构体指针:

实例

#include <stdio.h>

struct Point {
    int x;
    int y;
};

int main() {
    struct Point points[] = {{1, 2}, {3, 4}, {5, 6}};
    struct Point *ptr = points;  // 指针指向结构体数组的第一个元素

    printf("初始点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (1, 2)

    ptr++;  // 递增指针,使其指向下一个结构体
    printf("递增后点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (3, 4)

    return 0;
}

在这个示例中,ptr++ 使指针从 points[0] 指向 points[1]。因为 ptr 是一个 struct Point 类型指针,所以它递增时会移动 sizeof(struct Point) 个字节。

当上面的代码被编译和执行时,它会产生下列结果:

初始点: (1, 2)
递增后点: (3, 4)

递减一个指针

递减一个指针意味着让指针指向前一个内存位置。和递增指针类似,指针的递减操作也会根据指针所指向的数据类型进行适当的内存偏移。

对指针进行递减运算,即把值减去其数据类型的字节数,如下所示:

实例

#include <stdio.h> int main() { int arr[] = {10, 20, 30, 40, 50}; int *ptr = &arr[4]; // 指针指向数组的最后一个元素 printf("初始值: %d\n", *ptr); // 输出 50 ptr--; // 递减指针,使其指向前一个整数元素 printf("递减后值: %d\n", *ptr); // 输出 40 ptr--; // 再次递减指针 printf("再次递减后值: %d\n", *ptr); // 输出 30 return 0; }

当上面的代码被编译和执行时,它会产生下列结果:

初始值: 50
递减后值: 40
再次递减后值: 30

递减字符指针:

实例

#include <stdio.h>

int main() {
    char str[] = "Hello";
    char *ptr = &str[4];  // 指针指向字符串的最后一个字符 'o'

    printf("初始字符: %c\n", *ptr);  // 输出 o

    ptr--;  // 递减指针,使其指向前一个字符
    printf("递减后字符: %c\n", *ptr);  // 输出 l

    ptr--;  // 再次递减指针
    printf("再次递减后字符: %c\n", *ptr);  // 输出 l

    return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

初始字符: o
递减后字符: l
再次递减后字符: l

递减结构体指针:

实例

#include <stdio.h>

struct Point {
    int x;
    int y;
};

int main() {
    struct Point points[] = {{1, 2}, {3, 4}, {5, 6}};
    struct Point *ptr = &points[2];  // 指针指向结构体数组的最后一个元素

    printf("初始点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (5, 6)

    ptr--;  // 递减指针,使其指向前一个结构体
    printf("递减后点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (3, 4)

    ptr--;  // 再次递减指针
    printf("再次递减后点: (%d, %d)\n", ptr->x, ptr->y);  // 输出 (1, 2)

    return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

初始点: (5, 6)
递减后点: (3, 4)
再次递减后点: (1, 2)

指针的比较

在 C 语言中,可以比较指针来确定它们的关系。指针比较主要用于确定两个指针是否指向相同的内存位置或确定一个指针是否位于另一个指针之前或之后。

指针可以用关系运算符进行比较,如==!=<><=>=。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。

以下是一些示例代码,展示了如何比较指针并输出结果。

指针相等比较:

实例

#include <stdio.h> int main() { int a = 5; int b = 10; int *ptr1 = &a; int *ptr2 = &a; int *ptr3 = &b; if (ptr1 == ptr2) { printf("ptr1 和 ptr2 指向相同的内存地址\n"); // 这行会被输出 } else { printf("ptr1 和 ptr2 指向不同的内存地址\n"); } if (ptr1 != ptr3) { printf("ptr1 和 ptr3 指向不同的内存地址\n"); // 这行会被输出 } else { printf("ptr1 和 ptr3 指向相同的内存地址\n"); } return 0; }

当上面的代码被编译和执行时,它会产生下列结果:

ptr1 和 ptr2 指向相同的内存地址
ptr1 和 ptr3 指向不同的内存地址

指针大小比较:

实例

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr1 = &arr[1];  // 指向 arr[1],值为 20
    int *ptr2 = &arr[3];  // 指向 arr[3],值为 40

    if (ptr1 < ptr2) {
        printf("ptr1 在 ptr2 之前\n");  // 这行会被输出
    } else {
        printf("ptr1 在 ptr2 之后或相同位置\n");
    }

    if (ptr1 > ptr2) {
        printf("ptr1 在 ptr2 之后\n");
    } else {
        printf("ptr1 在 ptr2 之前或相同位置\n");  // 这行会被输出
    }

    return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

ptr1 在 ptr2 之前
ptr1 在 ptr2 之前或相同位置

遍历数组并比较指针:

实例

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *start = arr;           // 指向数组的第一个元素
    int *end = &arr[4];         // 指向数组的最后一个元素
    int *ptr;

    for (ptr = start; ptr <= end; ptr++) {
        printf("当前指针指向的值: %d\n", *ptr);
    }

    return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

当前指针指向的值: 10
当前指针指向的值: 20
当前指针指向的值: 30
当前指针指向的值: 40
当前指针指向的值: 50

总结

  • 相等比较 (==!=): 用于判断两个指针是否指向相同的内存位置。
  • 大小比较 (<, >, <=, >=): 通常用于指针遍历数组或内存块时,判断一个指针是否在另一个指针之前或之后。
需要注意的是,指针比较只有在指向同一个数组或同一内存块时才有意义,否则行为未定义。

C 指针 C 指针