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)
是地址常量.(好吧,我认为它们是.)那么什么上下文需要空指针常量而不接受地址常量?只有少数.
可以将函数指针类型的表达式与空指针常量的相等性进行比较.(可以将对象指针与类型的表达式进行比较void*
,但函数指针可能不会,除非它是空指针常量.)所以这样:
void func(void);
if (func == ((void*)0)) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
将违反约束条款.
在赋值中,空指针常量可以分配给指向函数类型的对象,并且将被隐式转换.void*
不是空指针常量的类型的表达式可能不会分配给函数指针.相同的约束适用于参数传递和初始化.所以这:
void (*fp)(void) = ((void*)0);
Run Code Online (Sandbox Code Playgroud)
如果((void*)0)
不是空指针常量,则会违反约束.感谢评论者hvd找到这个.
<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
.
它是一个带括号的表达式,它包含一个空指针常量,因此无可争议地是一个空指针值.将其用作右值与使用"兼容"版本作为r值具有完全相同的效果.
如果有一些语法规则只能接受空指针常量,那么就不符合条件.但我不知道(虽然我不太擅长C).
虽然两者都不是常量(指正式语法生成),但两者都可以出现在初始化器中的常量表达式中,因为允许空指针常量和地址常量,并且类别中明确包含常量空指针值的地址不变.
指针比较还特别提到了空指针常量......但是这里也接受指针值,并且所有空指针值都被平等对待.三元和赋值运算符也是如此.
请注意,这些规则在C++中是完全不同的,其中上述表达式都是类型的常量空指针值void*
,但不是通用空指针常量.C++中的空指针常量是整数常量表达式,其值为零.并且void*
不会隐式转换为其他指针类型.