为什么在取消引用空指针时必须进行强制转换?
我有这个例子:
int x;
void* px = &x;
*px = 9;
您能证明为什么这不起作用吗?
根据定义,void
指针指向我不确定对象的类型。
根据定义,当您使用一元*
运算符访问指针所指向的对象时,您必须知道(而且,编译器必须知道)对象的类型。
所以我们刚刚证明我们不能使用void
直接取消引用*
指针;我们必须始终始终首先将void
指针显式转换为某些实际对象指针类型。
[现在,在许多人的心中,“明显的”答案是“通用指针应该/应该指向什么类型?”是“ char
”。而且,曾几何时,在发明void
类型之前,通常将字符指针用作“通用”指针。因此,某些编译器(尤其是gcc)进行了一些扩展,使您可以使用void
指针执行比标准要求更多的工作(例如指针算术)。
以便might解释问题中类似的代码如何能够“起作用”。 (但是,在您的情况下,由于指向的类型实际上是int
,而不是char
,所以如果“起作用”是因为您使用的是低端字节序的计算机。)
...如此说来,我发现您问题中的代码对我来说是有效的,即使在gcc下也是如此。它首先给了我一个非致命的警告:警告:取消引用“ void *”指针
但是随后它改变了主意,并决定这是一个错误:
错误:无效使用void表达式我尝试的第二个编译器说了类似的内容:
错误:不完整类型'void'无法分配,在取消引用指针时需要使用指向类型:当您使用
附录:要详细说明为什么
*
访问指针时,编译器将发出代码以从指向的位置获取(或存储到)。但是编译器将不得不发出访问一定数量字节的代码,在许多情况下,如何解释这些字节可能很重要。字节的数量和解释都由类型(即[[for是什么类型)决定,这正是为什么需要实际的非void
类型的原因。我了解这种要求的最好方法之一就是考虑使用类似的代码
*p + 1
甚至更好
*p += 1
如果
p
指向char
,则编译器可能会发出某种addb
(“添加字节”)指令。如果p
指向int
,则编译器将发出一条普通的add
指令。如果p
指向float
或double
,则编译器将发出浮点加法指令。依此类推。
但是如果p
是void *
,则编译器不知道该怎么做。它抱怨(以错误消息的形式),不仅是因为C标准说您不能取消引用void
指针,而且更重要的是,因为编译器根本不知道如何处理您的代码。
[它可能不起作用,因为它违反了ISO C标准中的一个要求诊断的规则,并且(我猜)您的编译器将其视为致命情况。
*
运算符表示间接。如果操作数指向一个函数,则结果是一个函数指示符;如果它指向一个对象,则结果是一个指定对象的左值。如果操作数的类型为''指向“类型”的指针,结果为“类型”。[6.5.3.2¶4,n1548]类型void
既不是函数也不是对象类型,因此涉及产生函数或对象指示符的中间句子不适用于我们的情况。上文引用的最后一句适用。它要求取消引用void *
的表达式具有void
类型。
因此*px = 9;
搁浅,因为它正在为int
表达式分配void
值。赋值需要对象类型的左值表达式; void
不是对象类型,表达式肯定不是左值。约束的确切措辞是:“赋值运算符的左操作数应具有可修改的左值。” [6.5.16¶2,n1548]违反此约束要求进行诊断。
从我可能天真的对标准的阅读中看来,表达式*px
如此是有效的;无需尝试从中提取结果或将其用作分配的目标。如果是这样,则可以将其用作表达式语句,其值将被丢弃:if (foo()) { *px; }
,也可以将其冗余地转换为void
:(void) *px
。这些看似毫无意义的情况可能被某种类型的宏以某种方式利用,或至少在某些类型的宏中产生。例如,如果要确保某个宏的参数是指针,我们可以利用
*
需要指针操作数的约束:
#define MAC(NUM, PTR) ( ... (void) *(PTR) ...)
即在宏的某个地方,我们取消引用指针并丢弃结果,这将诊断PTR
是否不是指针。看起来ISO C允许这种用法,即使PTR
是void *
,这可能是有用的。