加粗的句子是不是和作者声称((void*)0)不是空指针常量相矛盾?
不,它没有。 (我承认有点偏见,因为引用的博客是我的。)
加粗的句子表示它的type和value与未加括号的表达式相同。这还不足以暗示它是一个空指针常量。
考虑:
void *var = 0;
(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)) { /* ... */ }
会违反约束。
6.5.16.1 简单赋值
在赋值中,一个空指针常量可以被赋值给一个指向函数类型的对象,并且会被隐式转换。不是空指针常量的void* 类型的表达式不能分配给函数指针。相同的约束适用于参数传递和初始化。所以这个:
void (*fp)(void) = ((void*)0);
如果((void*)0) 不是空指针常量,则将违反约束。感谢评论者 hvd 找到这个。
7.19 常用定义<stddef.h>
宏NULL 扩展为“实现定义的空指针常量”。如果((void*)0) 不是空指针常量,那么:
#define NULL ((void*)0)
无效。这将是对实现的限制,而不是对程序员的限制。请注意:
#define NULL (void*)0
绝对无效,因为标准头文件中的宏定义必须在必要时用括号完全保护(7.1.2p5)。如果没有括号,则有效表达式 sizeof NULL 将是一个语法错误,扩展为 sizeof (void*) 后跟一个无关常量 0。