我正在阅读this blog post,在空指针常量和括号表达式部分下,作者参考了ISO C标准中的第6.3.2.3节和第6.5.1节,并说:< / p>
它不说带括号的空指针常量是一个空指针常量。
严格来说,这意味着
(void*)0
是一个空指针 常数,但((void*)0)
不是。
然后:
我确信大多数C实现确实将带括号的空指针常量视为空指针常量,并将
NULL
定义为0
,((void*)0)
或以其他方式
两个参考部分说:
§6.3.2.3
值为0的整型常量表达式,或此类表达式 强制转换为void *,称为空指针常量。
§6.5.1
带括号的表达式是主表达式。 其类型和价值 与未表达的表达相同。这是一个 左值,函数指示符,或者如果是,则表示空值表达式 未表示的表达式分别是左值,一个函数 指示符,或无效表达。
粗体句是否与作者声称((void*)0)
不是空指针常量的说法相矛盾?
答案 0 :(得分:27)
粗体句是否与作者声称
((void*)0)
不是空指针常量的说法相矛盾?
不,它没有。 (我承认有点偏颇,因为引用的博客是我的。)
粗体句子表示其类型和值与未表示的表达式相同。这还不足以暗示它是一个空指针常量。
考虑:
void *var = 0;
(void*)0
是一个空指针常量。 ((void*)0)
与[{1}}具有相同的类型和值。 (void*)0
也与var
具有相同的类型和值,但(void*)0
显然不是空指针常量。
话虽如此,我99%确定 intent 是var
是一个空指针常量,更一般地说任何带括号的空指针常量都是空指针不变。该标准的作者只是忽略了这样说。由于6.5.1p5中带括号的表达式的描述具体列举了括号表达式继承的其他几个特性:
带括号的表达式是主表达式。它的类型和价值 与未表达的表达相同。它是一个 左值,函数指示符,或者如果是,则表示空值表达式 未表示的表达式分别是左值,一个函数 指示符,或无效表达。
遗漏令人不安(但只是稍微如此)。
但是,为了论证,我们假设((void*)0)
不是空指针常量。它有什么不同?
((void*)0)
是一个空指针常量,其值是类型为(void*)0
的空指针,因此通过括号表达式void*
的语义也有一个值为空指针的值输入((void*)0)
。 void*
和(void*)0
都是地址常量。 (好吧,我认为他们是。)那么什么上下文需要空指针常量并且不接受地址常量?只有少数。
可以将函数指针类型的表达式与空指针常量的相等性进行比较。 (可以将对象指针与类型((void*)0)
的表达式进行比较,但函数指针可能不会,除非它是空指针常量。)所以这个:
void*
会违反约束条件。
在赋值中,可以将空指针常量赋给指针到函数类型的对象,并且将隐式转换。不是空指针常量的类型void func(void);
if (func == ((void*)0)) { /* ... */ }
的表达式可能不会分配给函数指针。相同的约束适用于参数传递和初始化。所以这个:
void*
如果void (*fp)(void) = ((void*)0);
不是空指针常量,则会违反约束。感谢评论员hvd找到这个。
((void*)0)
宏<stddef.h>
扩展为“实现定义的空指针常量”。如果NULL
不是空指针常量,那么:
((void*)0)
无效。这将是对实现的限制,而不是对程序员的限制。请注意:
#define NULL ((void*)0)
肯定是无效的,因为标准头中的宏定义必须在必要时用括号完全保护(7.1.2p5)。如果没有括号,则有效表达式#define NULL (void*)0
将是语法错误,扩展为sizeof NULL
,后跟一个无关的常量sizeof (void*)
。
答案 1 :(得分:7)
它是一个带括号的表达式,它包含一个空指针常量,因此无可争议地是一个空指针值。将它用作右值与使用&#34;兼容&#34;具有完全相同的效果。版本为r值。
如果有一些语法规则只能 接受空指针常量,那么就不符合条件。但我并不知道(虽然我不太擅长C)。
虽然两者都不是常量(指正式语法生成),但两者都可以出现在初始化器中的常量表达式中,因为空指针常量和地址常量都是允许的,并且常量空指针值显式包含在地址常量的类别中。
指针比较还特别提到空指针常量......但是这里也接受指针值,并且所有空指针值都被平等对待。三元和赋值运算符也是如此。
请注意,这些规则在C ++中有很大不同,其中上述两个表达式都是void*
类型的常量空指针值,但不是通用空指针常量。 C ++中的空指针常量是整数常量表达式,其值为零。并且void*
不会隐式转换为其他指针类型。
答案 2 :(得分:-3)
尝试在您的C代码行下打印:
printf(“%p”,(void *)0);
您将得到的输出为:
(无)