使用void指针取消引用float变量:
#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
两者中,输出相同。当我们能够通过类型转换普通指针来解决不同的数据类型变量时,是什么原因?
答案 0 :(得分:14)
将任何数据指针转换为void*
指针并保证返回原始指针。
从C11标准草案N1570:
6.3.2.3指针
- 指向
醇>void
的指针可以转换为指向任何对象类型的指针。指向的指针 任何对象类型都可以转换为指向void的指针,然后再返回;结果应该 比较等于原始指针。
将数据指针转换为除void*
(示例中为int*
)之外的其他数据指针可能有效。这取决于您使用的编译器和您所使用的系统。并非所有系统都可能对不同的指针类型使用相同的内部表示。
- 指向对象类型的指针可以转换为指向不同对象类型的指针。如果 对于引用的类型,结果指针未正确对齐 68),行为是 未定义。否则,当再次转换回来时,结果应该等于 原始指针。当指向对象的指针转换为指向字符类型的指针时, 结果指向对象的最低寻址字节。连续增量 结果,直到对象的大小,产生指向对象剩余字节的指针。
醇>
这与严格别名规则不同。
float a = 7.5;
int *ip=&a;
int i = *ip; // Dereferenced using invalid type
上面的代码打破了严格的别名规则,因为解除引用的类型与原始类型不同。这会导致未定义的行为,并且始终是无效的代码。
答案 1 :(得分:5)
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段:
指向对象或不完整类型的指针可以转换为 指向其他对象或不完整类型的指针。如果结果 指针未正确对齐引用的类型,行为 未定义。
答案 2 :(得分:4)
void
指针是(有点)无类型的。它可以指向任何没有编译器抱怨的东西。例如如果你有一个int
变量,你可以安全地创建一个指向它的void
指针并传递它。 e.g。
int x = 10;
void *p = &x
很好,但
int x = 10;
float *p = &x;
会扰乱编译器
这对于在多个指针类型上运行的函数或者如果您将决定运行时的内容是特别有用的。
但是,无法直接取消引用void指针(*
),因为编译器不知道它们的类型。所以,
printf("%d\n", *p);
如果p是无效指针,将会中断。我们必须知道它取消引用的大小,这是使用手动类型转换(就像你已经完成的那样)完成的。
在您的特定情况下,您有一个指向浮动的指针,您可以在打印之前将其转换为浮动。所以,你会得到相同的输出。 void *
指针在这里并没有发挥重要作用。
您需要void *
的地方的示例是malloc
函数,如果您查看原型,它会返回void *
。即一块原始记忆。在进行指针算术和解除引用之前,需要将其作为具体类型进行类型转换。