c - 如果指针可以转换为任何类型(在 c 中),为什么要使用 void 指针?
问题描述
我想了解拥有 void 指针的真正需要,例如在下面的代码中,我使用强制转换能够以不同的方式使用相同的 ptr,那么如果可以强制转换,为什么真的有一个 void 指针?
int main()
{
int x = 0xAABBCCDD;
int * y = &x;
short * c = (short *)y;
char * d = (char*)y;
*c = 0;
printf("x is %x\n",x);//aabb0000
d +=2;
*d = 0;
printf("x is %x\n",x);//aa000000
return 0;
}
解决方案
基本 C 不支持将任何指针类型转换为任何其他指针类型(即,没有 C 标准不需要的任何扩展或行为的 C)。2018 C 标准在第 6.3.2.3 条第 7 段中说:
指向对象类型的指针可以转换为指向不同对象类型的指针。如果结果指针未正确对齐引用的类型,则行为未定义。否则,当再次转换回来时,结果将等于原始指针......</p>
在那篇文章中,我们看到了两个限制:
- 如果指针未正确对齐,则转换可能会以各种方式失败。在您的示例中,将 a 转换
int *
为 ashort *
不太可能失败,因为int
通常比short
. 但是,基本 C 不支持反向转换。假设您使用short x[20];
or定义了一个数组char x[20];
。然后数组将根据 ashort
or的需要对齐char
,但不一定按照a 的需要对齐,在这种情况下,C 标准不会定义int
of 的行为。(int *) x
- 转换产生的值大多未指定。这段话只保证将它转换回来会产生原始指针(或等效的东西)。它不保证您可以在不将指针转换回来的情况下对指针做任何有用的事情——您不一定要使用转换自的指针
int *
来访问short
.
该标准确实对某些指针转换做出了一些额外的保证。其中之一是上述段落的延续:
…当指向对象的指针转换为指向字符类型的指针时,结果指向对象的最低寻址字节。结果的连续增量,直到对象的大小,产生指向对象剩余字节的指针。
因此,您可以使用转换 from 的指针int *
来访问表示 的各个字节int
,并且您可以执行相同操作来访问任何其他对象类型的字节。但该保证仅适用于使用字符类型而不是类型访问单个字节short
。
从上面我们知道,short * c = (short *)y;
在你的例子中,在你的例子之后,y
不一定指向x
它起源的任何部分——指针转换产生的值根本不能保证作为 a 工作short *
。但是,即使它确实指向所在的地方x
,base C 也不支持使用c
来访问这些字节,因为 6.5 7 说:
对象的存储值只能由具有以下类型之一的左值表达式访问:
— 与对象的有效类型兼容的类型,
— 与对象的有效类型兼容的类型的限定版本,
— 与对象的有效类型相对应的有符号或无符号类型,
— 对应于对象有效类型的限定版本的有符号或无符号类型,
— 在其成员中包含上述类型之一的聚合或联合类型(递归地,包括子聚合或包含联合的成员),或
— 一种字符类型。
因此*c = 0;
,C 不支持您的示例中的 ,原因有两个:c
不一定指向任何有效地址的任何部分x
或任何有效地址,并且即使它确实如此,修改部分int
x
usingshort
类型的行为也不是由 C 定义的标准。它可能似乎在您的 C 实现中工作,甚至可能由您的 C 实现支持,但它并非严格符合 C 代码。
C 标准提供了void *
在特定类型不足时使用的类型。6.3.2.3 1 对指针的保证与void
对对象指针的保证类似:
指向的指针
void
可以转换为指向任何对象类型的指针或从指向任何对象类型的指针转换。指向任何对象类型的指针都可以转换为指向void
和返回的指针;结果应与原始指针比较。
void *
与必须与任意对象类型一起使用的例程一起使用,例如qsort
. char *
可以达到这个目的,但最好有一个单独的类型,明确表示没有特定类型与之关联。例如,如果函数的参数是char *p
,则该函数可能会无意中使用*p
并获取它不想要的字符。如果参数是void *p
,则函数必须在使用指针访问对象之前将指针转换为特定类型。因此,为“通用指针”设置一个特殊类型可以帮助避免错误,并向阅读代码的人表明意图。
推荐阅读
- terraform - 无法通过 terraform 为 Azure 虚拟机设置动态 IP
- types - f# 表达式的类型
- kotlin - 下面代码的 reader.next()[0] 行中 [0] 的作用是什么
- c - 为了让我的 C 程序更加用户友好,需要进行哪些更改?
- c# - 不支持 WCF 服务操作类型
- javascript - Nodejs,如何在同一行显示和更新文本,例如运行时钟或下载速度?
- perl - 我可以用perl中的子类比较来调用超类排序吗?
- qt - 如果在 QWidget 中创建 QQuickWidget,则 Qt qml 崩溃
- html - 如何创建包含 4 个部分的页面布局?
- kubernetes - kubernetes Pod 的 readinessProbe 出错但端点未从服务中删除