((void*)0)是否为空指针常量?

use*_*058 35 c language-lawyer

我正在阅读这篇博客文章,在Null指针常量和括号表达式部分下,作者参考了ISO C标准中的第6.3.2.3节和第6.5.1节,并说:

没有说括号空指针常量是一个空指针常量.

严格来说,这意味着,这(void*)0是一个空指针常量,但((void*)0)不是.

然后:

我敢肯定,大多数C实现根本治疗括号空指针常数作为空指针常数,并定义NULL无论是作为0,((void*)0)或以其他的方式.

两个参考部分说:

§6.3.2.3

值为0的整型常量表达式或类型为void*的表达式称为空指针常量.

§6.5.1

带括号的表达式是主表达式.它的类型和值与未表示的表达式相同.如果未表示的表达式分别是左值,函数指示符或空表达式,则它是左值,函数指示符或void表达式.

粗体句是否与提交人声称((void*)0)不是空指针常量相矛盾?

Kei*_*son 28

粗体句是否与提交人声称((void*)0)不是空指针常量相矛盾?

不,它没有.(我承认有点偏颇,因为引用的博客是我的.)

粗体句子说它的类型与未表示的表达式相同.这还不足以暗示它是一个空指针常量.

考虑:

void *var = 0;
Run Code Online (Sandbox Code Playgroud)

(void*)0是一个空指针常量.((void*)0)具有相同的类型和价值(void*)0.var 具有相同的类型和值(void*)0,但var显然不是空指针常量.

话虽如此,我99%确定意图((void*)0)空指针常量,更一般地说任何带括号的空指针常量是空指针常量.该标准的作者只是忽略了这样说.由于6.5.1p5中括号表达式的描述具体列举了括号表达式继承的其他几个特性:

带括号的表达式是主表达式.它的类型和值与未表示的表达式相同.如果未表示的表达式分别是左值,函数指示符或空表达式,则它是左值,函数指示符或void表达式.

遗漏令人不安(但只是轻微的).

但是,为了论证,我们假设这((void*)0)不是一个空指针常量.它有什么不同?

(void*)0是一个空指针常量,其值是类型的空指针void*,因此通过括号表达式的语义((void*)0)也有一个值为类型的空指针void*.这两个(void*)0((void*)0)地址常量.(好吧,我认为它们是.)那么什么上下文需要空指针常量而不接受地址常量?只有少数.

6.5.9平等运营商

可以将函数指针类型的表达式与空指针常量的相等性进行比较.(可以将对象指针与类型的表达式进行比较void*,但函数指针可能不会,除非它是空指针常量.)所以这样:

void func(void);
if (func == ((void*)0)) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

将违反约束条款.

6.5.16.1简单分配

在赋值中,空指针常量可以分配给指向函数类型的对象,并且将被隐式转换.void*不是空指针常量的类型的表达式可能不会分配给函数指针.相同的约束适用于参数传递和初始化.所以这:

void (*fp)(void) = ((void*)0);
Run Code Online (Sandbox Code Playgroud)

如果((void*)0)不是空指针常量,则会违反约束.感谢评论者hvd找到这个.

7.19通用定义 <stddef.h>

NULL扩展为"实现定义的空指针常量".如果((void*)0)不是空指针常量,那么:

#define NULL ((void*)0)
Run Code Online (Sandbox Code Playgroud)

会无效的.这将是对实现的限制,而不是对程序员的限制.请注意:

#define NULL (void*)0
Run Code Online (Sandbox Code Playgroud)

肯定是无效的,因为标准头中的宏定义必须在必要时用括号完全保护(7.1.2p5).如果没有括号,则有效表达式sizeof NULL将是语法错误,扩展为sizeof (void*)后跟一个无关的常量0.

  • 你能详细说明"不是空指针常量"是什么意思吗?我的意思是,'var`不是一个因为它不是一个常量,但它对于`((void*)0)`不是一个意味着什么?由于这种疏忽,规范在技术上是否存在任何类型的反常编译器行为? (2认同)
  • @KeithThompson 6.3.2.3第4节:_对另一个指针类型的空指针的转换产生该类型的空指针.任何两个空指针都应该相等._ (2认同)
  • 仅仅将`((void*)0)`转换为指向函数的指针就足以证明问题,即使没有任何比较:`void(*fp)(void)=((void*)0);`当且仅当`((void*)0)`是NPC时才有效.也许有趣的是,C++已经用更简单的语法替换了括号的语义:"括号表达式可以在与可以使用封闭表达式的语境完全相同的上下文中使用,并且具有相同的含义,除非另有说明".这也会影响`char str [] =("abc");`,它在C++中有效但在C中无效. (2认同)

Ben*_*igt 7

它是一个带括号的表达式,它包含一个空指针常量,因此无可争议地是一个空指针值.将其用作右值与使用"兼容"版本作为r值具有完全相同的效果.

如果有一些语法规则只能接受空指针常量,那么就不符合条件.但我不知道(虽然我不太擅长C).

虽然两者都不是常量(指正式语法生成),但两者都可以出现在初始化器中的常量表达式中,因为允许空指针常量和地址常量,并且类别中明确包含常量空指针值的地址不变.

指针比较还特别提到了空指针常量......但是这里也接受指针值,并且所有空指针值都被平等对待.三元和赋值运算符也是如此.

请注意,这些规则在C++中是完全不同的,其中上述表达式都是类型的常量空指针值void*,但不是通用空指针常量.C++中的空指针常量是整数常量表达式,其值为零.并且void*不会隐式转换为其他指针类型.

  • 事实证明,至少有一个上下文允许空指针常量但是`((void*)0)`,如果它不是NPC则不会.可以将指向函数类型的表达式的等式或不等式与空指针常量进行比较,但不与任何其他类型为"void*"的常量表达式进行比较.有关详情,请参阅我的更新答案 (2认同)