我注意到GCC触发器:
warning: dereferencing ‘void *’ pointer
同时采用已取消引用的void
表达式的地址,例如:
int main()
{
void *p = "abcdefgh";
printf("%p\n", p);
printf("%p\n", &*p);
return 0;
}
但是,根据C标准,表达式p
等效于&*p
:
§6.5.3.2地址和间接运算符
一元&运算符返回其操作数的地址。如果操作数的类型为“类型”,则结果的类型为“要输入的指针”。如果操作数是一元*运算符的结果,则不会对该运算符和&运算符求值,并且结果似乎都被省略了,除了运算符上的约束仍然适用并且结果不是左值。>
了解Clang不会触发此警告也很重要。
免责声明
为获得更好的解释,请考虑以下问题:
int main()
{
int *p = NULL;
printf("%p\n", (void *) p);
printf("%p\n", (void *) &*p);
return 0;
}
此代码可与Clang和GCC一起编译并完美运行。 C标准对void
指针很明确,您可以取消引用它们(从而获得一个void
值),但不能使用该表达式的结果。
答案 0 :(得分:2)
我假设这里的问题是“为什么?/给什么?”。 在那种情况下,这实际上是一些标准的措辞/为开发人员提供灵活性。
严格来说,*(void*)
总是很糟糕,编译器可以警告您。
在实践中,正如您正确指出的那样,在评估指针所指向的内容时,指针实际上从未真正被取消引用,因此,天真的编译器实现将以这种方式查看并毫无问题地进行掩饰这并说“指针:它们是数字吗?这个是0xFFF ....”。
在实践中,尽管有时并非如此。有时候编译器变得很聪明。没有想到好的例子,但这不是重点。
关于“它从未被评估”,我认为可能需要注意以下几点:
给予编译器自由执行其所需功能的原因,部分是为了简化编译过程本身,而不仅仅是输出更好的实现。 您指向的下一个编译器可能会拒绝。只是因为它更容易,而且没有必要。
第二,这并不是警告的重点。如果我在代码审查中看到了这一点:
int i = 3;
if (3-i) {
((int*)(NULL)) += 4;
}
我的反应不会是:“哦,很好,没有评估。”但是“哇!”。这也是编译器所做的。它告诉您,它认为您可能已经做了您可能需要重新考虑的事情,在这种情况下,称const char *
为void *
。我同意gcc。