为什么要使用 void 指针来取消引用数据类型的变量?

Why use a void pointer for dereferencing variables of datatypes?

使用 void 指针取消引用浮点变量:

#include <stdio.h>

int main() {
    float a = 7.5;
    void *vp = &a;
    printf("%f", *(float*)vp); /* Typecasting a void pointer to float for dereference */
    printf("\n");
}

输出:7.500000

使用整数指针取消引用变量:

#include <stdio.h>

int main() {
    float a = 7.5;
    int *ip = &a;
    printf("%f", *(float*)ip); /* Typecasting an int pointer to float for dereference */
    printf("\n");
}

输出:7.500000

两者的输出相同。当我们能够通过类型转换普通指针来实现时,为什么要取消对不同数据类型变量的引用?

将任何数据指针转换为 void* 指针并返回可以保证返回原始指针。

来自 C11 标准草案 N1570:

6.3.2.3 Pointers

  1. A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

将数据指针转换为除 void*(在您的示例中为 int*)之外的其他数据指针 可能 有效。这取决于您使用的编译器和您所在的系统。并非所有系统都可能对不同的指针类型使用相同的内部表示。

  1. A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned 68) for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.

这不同于严格的别名规则

float a = 7.5;    
int *ip=&a;
int i = *ip; // Dereferenced using invalid type

上面的代码违反了严格的别名规则,因为取消引用的类型与原始类型不同。这会导致 未定义的行为 并且始终是无效代码。

一个void指针是一个泛型指针可以保存任何类型的地址,可以是类型转换到任何类型。

在第一种情况下,程序成功编译并且 运行 没有任何警告或错误,因为使用 void 指针将一种指针类型转换为另一种指针类型,然后将其存储或转换为最终类型是安全的,不会丢失数据。

但在第二种情况下,GCC 编译器生成了警告

prog.c: In function 'main':
prog.c:5:9: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
 int *ip=&a;
     ^

clang 编译器:

warning: incompatible pointer types initializing 'int *' with an expression of type 'float *' [-Wincompatible-pointer-types]
int *ip=&a;
     ^  ~~

C11 标准,6.3.2.3,第 7 段:

A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

A void 指针是(某种程度上)未类型化的。它可以指向任何东西而编译器不会抱怨。例如如果你有一个 int 变量,你可以安全地创建一个指向它的 void 指针并传递它。例如

int x = 10;
void *p = &x 

很好但是

int x = 10;
float *p = &x; 

会扰乱编译器

这对于操作多个指针类型的函数或者如果您将在运行时决定什么是什么特别有用。

但是,不能直接取消引用 void 指针 (*),因为编译器不知道它们的类型。所以,

printf("%d\n", *p); 

如果 p 是空指针,将会中断。我们必须知道它指向的内容的大小才能取消引用它,这是使用手动类型转换完成的(就像你所做的那样)。

在您的特定情况下,您有一个指向浮点数的指针,您在打印之前将其类型转换回浮点数。因此,您将获得相同的输出。 void * 指针在这里并没有真正起到很大的作用。

您需要 void * 的一个示例是 malloc 函数,如果您查看原型,它 returns 一个 void *。即一块原始内存。在进行指针运算和取消引用之前,您需要将其转换为具体类型。