C语言 指针运算和 void 指针

C 语言中,指针运算 是指对指针进行的操作,比如递增、递减或两个指针相减。然而,void 指针在 C 中没有定义大小,因为 void 本身并不表示一个特定的类型。因此,不能像操作其他类型的指针那样直接对 void 指针进行指针运算。指针运算涉及到指针与它所指向对象的大小相乘。由于 void 指针没有大小(因为 void 是一个不完全类型),因此不能直接对 void 指针执行诸如递增(ptr++)或递减(ptr--)等运算,因为编译器无法确定它指向的数据的大小。

1、指针运算

指针运算是指对指针进行加法、减法、比较等操作。指针本身是存储内存地址的变量,指针运算就是对内存地址进行操作,通常用于遍历数组和动态内存分配。

1)指针加法(指针递增)

当对指针执行加法操作时,指针将增加一个元素的大小,而不是字节。比如,对于一个 int* 指针,指针加1实际上是将指针向前移动 sizeof(int) 字节的位置。

#include <stdio.h>

int main() {
    // 初始化数组
    int arr[] = {1, 2, 3};
    
    // 指针指向数组的第一个元素
    int* ptr = arr;

    // 打印原始数组元素
    printf("原始数组元素:\n");
    for (int i = 0; i < 3; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    // 使指针 ptr 指向 arr[1]
    ptr++;

    // 打印指针指向的元素
    printf("\n指针指向的新位置:\n");
    printf("ptr 指向的元素: %d\n", *ptr);  // ptr 现在指向 arr[1]

    // 继续移动指针到 arr[2]
    ptr++;

    // 打印指针指向的元素
    printf("ptr 指向的元素: %d\n", *ptr);  // ptr 现在指向 arr[2]

    return 0;
}

2)指针减法(指针递减)

与加法相反,指针减法使指针向后移动,指向前一个元素。

#include <stdio.h>

int main() {
    // 初始化数组
    int arr[] = {10, 20, 30, 40, 50};

    // 创建指针,指向 arr[2]
    int* ptr2 = arr + 2;

    // 输出指针指向的元素
    printf("ptr2 指向 arr[2]: %d\n", *ptr2);  // 输出 30

    // ptr2 指向前一个元素,即 arr[1]
    ptr2--;

    // 输出指针指向的新位置的元素
    printf("ptr2 现在指向 arr[1]: %d\n", *ptr2);  // 输出 20

    return 0;
}

3)指针之间的运算

指针相减时,结果是指针之间的差值,以元素的个数为单位,而不是字节数。

#include <stdio.h>

int main() {
    // 定义一个整数数组
    int arr[] = {10, 20, 30, 40, 50};

    // 指针 ptr1 指向数组的第一个元素
    int* ptr1 = arr;

    // 指针 ptr2 指向数组的第三个元素 (索引为2)
    int* ptr2 = arr + 2;

    // 计算 ptr2 和 ptr1 之间的距离 (以元素为单位)
    ptrdiff_t diff = ptr2 - ptr1;

    // 输出 ptr2 和 ptr1 之间的差值
    printf("diff = %td\n", diff);  // diff = 2

    return 0;
}

2、void 指针

void 指针是一个特殊类型的指针,它可以指向任何类型的数据,但是不能直接解引用,因为它不指定指向的数据类型。为了对

void 指针进行操作,必须先将其转换为某个具体类型的指针。void* 可以指向任何类型的数据,但不能直接进行解引用或指针运算。

1)void 指针的使用

为了使 void 指针能够解引用,必须先将它转换为其他类型的指针。

#include <stdio.h>

int main() {
    int num = 10;
    
    // 将 num 的地址赋值给 void* 指针
    void* ptr = #

    // 将 void* 转换为 int* 类型并解引用,输出 num 的值
    printf("%d\n", *(int*)ptr);  // 输出 10
    
    return 0;
}

2)void 指针与数组

void 指针不能直接进行指针运算,因此通常会先将其转换为特定类型的指针来进行运算。

#include <stdio.h>

int main() {
    int arr[] = {1, 2, 3}; 

    // 将 arr 的地址赋值给 void* 指针
    void* ptr = arr;

    // 需要先将 void* 转换为 int* 然后进行指针运算
    printf("%d\n", *((int*)ptr + 1));  // 输出 2

    return 0;
}

3)void 指针的常见用途

通用函数接口,如 malloc() 函数返回的是 void*,它可以指向任何类型的数据。回调函数通常通过 void* 来传递不确定类型的参数。

#include <stdio.h>

// 回调函数类型
typedef void (*Callback)(void*);

// 回调函数示例1:打印整数
void printInt(void* data) {
    int* ptr = (int*)data;
    printf("Integer: %d\n", *ptr);
}

// 回调函数示例2:打印字符串
void printString(void* data) {
    char* str = (char*)data;
    printf("String: %s\n", str);
}

// 通用的函数,接受回调函数和数据
void applyOperation(Callback callback, void* data) {
    callback(data);  // 调用回调函数
}

int main() {
    int num = 42;
    char str[] = "Hello, world!";

    // 使用不同类型的数据调用相同的函数
    applyOperation(printInt, &num);       // 输出:Integer: 42
    applyOperation(printString, str);     // 输出:String: Hello, world!

    return 0;
}

3、对 void 指针进行指针运算

为了对 void 指针进行指针运算,需要将 void 指针强制转换为某种特定类型的指针。因为编译器需要知道指针所指向数据的大小,以便在进行指针运算时正确计算内存地址。

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    void *ptr = arr;  // `void` 指针指向数组的开始
    size_t n = 3;  // 移动 3 个元素

    // 将指针递增 `n` 个 `int` 类型的元素
    ptr = (int *)ptr + n;

    // 现在 `ptr` 指向 `arr[3]`
    printf("指针指向的值是: %d\n", *(int *)ptr);  // 输出: 40

    return 0;
}

推荐阅读
cjavapy编程之路首页