多数指针带有类型信息,例如 int * 表示指向 int 的指针。当需要在不同时刻保存不同类型对象的地址,或编写与具体类型无关的接口时,可使用void 指针(void *)。本节说明其声明、赋值规则及与动态内存的关系。

类型不同的指针不能随意混赋

int i = 9;
int *p;
float *fp;

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

不同对象类型的指针之间不能直接赋值,除非经过兼容的类型转换。

void 指针的声明与赋值

void * 可保存任意类型对象的地址,且可将 void * 赋给任意类型的指针而不必显式转换(C 标准允许):

void *ptr;
int *p;
float *fp;
int i = 9;

ptr = p;
ptr = fp;
ptr = &i;

p = ptr;     /* void * 赋回 int * */

void 指针的大小

指针大小由平台决定,与所指类型无关:

#include <stdio.h>

int main(void) {
    void *ptr = NULL;
    int *p = NULL;
    char *cp = NULL;
    float *fp = NULL;

    printf("void * 的大小 = %zu\n", sizeof(ptr));
    printf("int * 的大小   = %zu\n", sizeof(p));
    printf("char * 的大小  = %zu\n", sizeof(cp));
    printf("float * 的大小 = %zu\n", sizeof(fp));
    return 0;
}

解引用前须转换为具体类型

void * 不携带元素类型信息,不能直接对 *ptr 读写,应先转换为相应类型的指针再解引用:

#include <stdio.h>

int main(void) {
    int a = 56;
    float b = 4.5f;
    char c = 'k';
    void *ptr;

    ptr = &a;
    printf("a 的值为: %d\n", *((int *)ptr));

    ptr = &b;
    printf("b 的值为: %f\n", *((float *)ptr));

    ptr = &c;
    printf("c 的值为: %c\n", *((char *)ptr));
    return 0;
}

同一 void * 变量可先后指向不同类型对象,读取时必须与当前实际类型一致,否则属于未定义行为。

常见用途

  • 动态内存:malloc、calloc 等返回 void *,再赋给具体类型指针,见动态内存分配。
  • 通用接口:如标准库 qsort 的比较函数通过 const void * 传递元素地址,由用户按元素类型解释。
int *x = (int *)malloc(sizeof(int));
if (x != NULL) {
    *x = 90;
    printf("%d\n", *x);
    free(x);
}

void * 用于存放“某处内存的地址”;真正读写时须恢复为正确的指针类型。掌握这一点有助于理解动态分配与部分标准库函数的设计。