普通指针都有类型,int 指针只能指 int。但有些场景你压根不知道会用哪种类型,这时 void 指针出场了。下面看它是什么、怎么用、一般拿来干嘛。

问题:指针类型卡得死

之前我们见过的指针,地址和声明类型必须匹配:

int i = 9;      // 整型变量
int *p;         // 整数指针
float *fp;      // 浮点指针

p = fp;         // 错误:类型不匹配
fp = &i;        // 错误:类型不匹配

int 指针不能指 float 变量,反之亦然。为了打破这种限制,C 语言提供了 void 指针。

void 指针是什么

void 指针是不限定类型的通用指针,可以保存任何数据类型的地址:

void *ptr;      // void 指针声明

地址可以被赋给它,也能赋回任意类型的指针,都不用显式类型转换:

ptr = p;        // 正确
ptr = fp;       // 正确
ptr = &i;       // 正确

void 指针的大小

void 指针的大小和字符指针一样,具体字节随平台变(32 位一般是 4,64 位一般是 8):

#include <stdio.h>

int main() {
    void *ptr = NULL;   // void 指针
    int *p = NULL;      // 整型指针
    char *cp = NULL;    // 字符指针
    float *fp = NULL;   // 浮点指针

    printf("void指针的大小 = %d\n", sizeof(ptr));
    printf("整型指针的大小 = %d\n", sizeof(p));
    printf("字符指针的大小 = %d\n", sizeof(cp));
    printf("浮点指针的大小 = %d\n", sizeof(fp));
    return 0;
}

所有指针的大小都一样,指针存的只是一个地址。

void 指针解引用前要转回原类型

void 指针不知道自己到底指着什么,所以直接 *ptr 是错的,必须先转回原类型再解引用:

#include <stdio.h>

int main() {
    int a = 56;         // 初始化一个整数变量
    float b = 4.5;      // 浮点变量
    char c = 'k';       // 字符变量
    void *ptr;          // 声明 void 指针

    // 给变量 a 分配地址
    ptr = &a;
    printf("a 的值为 : %d", *((int *)ptr));

    // 给变量 b 分配地址
    ptr = &b;
    printf("\nb 的值为 : %f", *((float *)ptr));

    // 给变量 c 分配地址
    ptr = &c;
    printf("\nc 的值为 : %c", *((char *)ptr));
    return 0;
}

输出:

a 的值为 : 56
b 的值为 : 4.500000
c 的值为 : k

同一个 void 指针先后指向 int、float、char,读取时各自转回对应类型。

void 指针有什么用

  • malloc()/calloc() 返回 void 指针:动态内存分配函数返回 void*,可以赋给任何类型的指针(通常写成 (int *)malloc(...)),这是它最常见的用途。
  • 实现泛型函数:参数用 void *,一套代码接收任意类型的数据。比如 qsort() 的比较函数、pthread 的线程函数参数统统是 void*。
  • 可重用性:void 指针能存任何类型的对象地址,配合适当类型转换即可取出。
// malloc 返回 void*,赋给 int*
int *x = (int *)malloc(sizeof(int));
*x = 90;
printf("%d", *x);   // 90

void 指针是不知道里面装什么的通用指针,随便收地址,但解引用前必须转回原类型;malloc 和泛型函数全靠它。