你能证明为什么当我引用一个空指针时,投射很重要吗?

问题描述 投票:-1回答:3

为什么在取消引用空指针时必须进行强制转换?

我有这个例子:

int x;
void* px = &x;
*px = 9;

您能证明为什么这不起作用吗?

c pointers casting void
3个回答
3
投票

根据定义,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指向floatdouble,则编译器将发出浮点加法指令。依此类推。

但是如果pvoid *,则编译器不知道该怎么做。它抱怨(以错误消息的形式),不仅是因为C标准说您不能取消引用void指针,而且更重要的是,因为编译器根本不知道如何处理您的代码。

2
投票

0
投票

[它可能不起作用,因为它违反了ISO C标准中的一个要求诊断的规则,并且(我猜)您的编译器将其视为致命情况。

0
投票
“一元*运算符表示间接。如果操作数指向一个函数,则结果是一个函数指示符;如果它指向一个对象,则结果是一个指定对象的左值。如果操作数的类型为''指向“类型”的指针,结果为“类型”。[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允许这种用法,即使PTRvoid *,这可能是有用的。

© www.soinside.com 2019 - 2024. All rights reserved.