I'm reading this blog post and under the section Null pointer constants and parenthesized expressions the author references § 6.3.2.3 and § 6.5.1 from the ISO C standard and says:
我阅读这篇文章,下一节空指针常量和带括号表达式作者参考§6.3.2.3和§6.5.1从ISO C标准,说:
It doesn't say that a parenthesized null pointer constant is a null pointer constant.
它并不是说括号中的空指针常量是空指针常量。
Which implies, strictly speaking, that
(void*)0
is a null pointer constant, but((void*)0)
is not.严格地说,这意味着(void*)0是一个空指针常量,但是((void*)0)不是。
Then:
然后:
I'm sure that most C implementations do treat a parenthesized null pointer constant as a null pointer constant, and define
NULL
either as0
,((void*)0)
, or in some other manner.我确信大多数C实现都将带括号的空指针常量作为空指针常量,并将null定义为0 (void*)0,或者以其他方式定义为0。
The two referenced sections say:
这两个参考的部分说:
§ 6.3.2.3
§6.3.2.3
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
一个值为0的整数常量表达式,或者一个类型为void *的表达式,称为空指针常量。
§ 6.5.1
§6.5.1
A parenthesized expression is a primary expression. Its type and value are identical to those of the unparenthesized expression. It is an lvalue, a function designator, or a void expression if the unparenthesized expression is, respectively, an lvalue, a function designator, or a void expression.
括号内的表达式是主表达式。它的类型和值与无括号表达式的类型和值相同。如果非插入式表达式分别是lvalue、函数指定器或空表达式,则它是lvalue、函数指定器或空表达式。
Doesn't the bolded sentence contradict the author's claim that ((void*)0)
is not a null pointer constant?
粗体字句子是否与作者声称(void*)0不是空指针常数的说法相矛盾?
2 个解决方案
#1
25
Doesn't the bolded sentence contradict the author's claim that
((void*)0)
is not a null pointer constant?粗体字句子是否与作者声称(void*)0不是空指针常数的说法相矛盾?
No, it doesn't. (I confess to being a bit biased, since the referenced blog is mine.)
不,它不是。(我承认我有点偏见,因为引用的博客是我的。)
The bolded sentence says that its type and value are identical to those of the unparenthesized expression. That's not enough to imply that it's a null pointer constant.
粗体字表示它的类型和值与无括号的表达式相同。这还不足以说明它是一个空指针常数。
Consider:
考虑:
void *var = 0;
(void*)0
is a null pointer constant. ((void*)0)
has the same type and value as (void*)0
. var
also has the same type and value as (void*)0
, but var
clearly is not a null pointer constant.
0是一个空指针常数。((void*)0)具有与(void*)0相同的类型和值。var的类型和值也与(void*)0相同,但是var显然不是空指针常量。
Having said that, I'm 99+% sure that the intent is that ((void*)0)
is a null pointer constant, and more generally that any parenthesized null pointer constant is a null pointer constant. The authors of the standard merely neglected to say so. And since the description of parenthesized expressions in 6.5.1p5 specifically enumerates several other characteristics that are inherited by parenthesized expressions:
话虽如此,我还是99+%地确信(void*)0是一个空指针常量,更一般地说,任何括号内的空指针常量都是一个空指针常量。《标准》的作者们只是忽略了这一点。由于在6.5.1p5中对括号内表达式的描述特别列举了由括号内表达式继承的其他几个特征:
A parenthesized expression is a primary expression. Its type and value are identical to those of the unparenthesized expression. It is an lvalue, a function designator, or a void expression if the unparenthesized expression is, respectively, an lvalue, a function designator, or a void expression.
括号内的表达式是主表达式。它的类型和值与无括号表达式的类型和值相同。如果非插入式表达式分别是lvalue、函数指定器或空表达式,则它是lvalue、函数指定器或空表达式。
the omission is troubling (but only mildly so).
遗漏是令人不安的(但只是轻微的)。
But let's assume, for the sake of argument, that ((void*)0)
is not a null pointer constant. What difference does it make?
但我们假设(void*)0不是空指针常数。这有什么区别?
(void*)0
is a null pointer constant, whose value is a null pointer of type void*
, so by the semantics of parenthesized expressions ((void*)0)
also has a value that is a null pointer of type void*
. Both (void*)0
and ((void*)0)
are address constants. (Well, I think they are.) So what contexts require a null pointer constant and do not accept an address constant? There are only a few.
(void*)0是一个空指针常量,其值是void*类型的空指针,因此根据圆括号表达式(void*)0的语义,它的值也是void*类型的空指针。(void*)0和(void*)0都是地址常量。(嗯,我想是的。)那么,什么上下文需要一个空指针常量而不接受地址常量呢?只有几个。
6.5.9 Equality operators
An expression of function pointer type may be compared for equality to a null pointer constant. (An object pointer may be compared to an expression of type void*
, but a function pointer may not, unless it's a null pointer constant.) So this:
可以将函数指针类型的表达式与空指针常数进行比较。(对象指针可以与void*类型的表达式进行比较,但函数指针则不能,除非它是一个空指针常量。)所以这个:
void func(void);
if (func == ((void*)0)) { /* ... */ }
would be a constraint violation.
会违反约束。
6.5.16.1 Simple assignment
In an assignment, a null pointer constant may be assigned to an object of pointer-to-function type, and will be implicitly converted. An expression of type void*
that's not a null pointer constant may not be assigned to a function pointer. The same constraints apply to argument passing and initialization. So this:
在赋值中,可以将空指针常量分配给指针到函数类型的对象,并将其隐式转换。非空指针常量的void*类型的表达式可能不会分配给函数指针。同样的约束适用于参数的传递和初始化。所以这个:
void (*fp)(void) = ((void*)0);
would be a constraint violation if ((void*)0)
were not a null pointer constant. Thanks to commenter hvd for finding this.
如果(void*)0不是空指针常量,则会违反约束。感谢hvd评论者的发现。
7.19 Common definitions <stddef.h>
The macro NULL
expands to "an implementation-defined null pointer constant". If ((void*)0)
is not a null pointer constant, then this:
宏NULL扩展为“实现定义的空指针常量”。如果(void*)0不是空指针常量,则如下:
#define NULL ((void*)0)
would be invalid. This would be a restriction imposed on the implementation, not on programmers. Note that this:
将是无效的。这将是对实现的限制,而不是对程序员。注意:
#define NULL (void*)0
is definitely invalid, since macro definitions in standard headers must be fully protected by parentheses where necessary (7.1.2p5). Without the parentheses, the valid expression sizeof NULL
would be a syntax error, expanding to sizeof (void*)
followed by an extraneous constant 0
.
是绝对无效的,因为标准头中的宏定义必须在必要时由圆括号完全保护(7.1.2p5)。如果没有括号,有效的表达式sizeof NULL将是一个语法错误,扩展到sizeof (void*),然后是一个无关的常量0。
#2
6
It is a parenthesized expression which contains a null pointer constant, so it indisputably is a null pointer value. Using it as an rvalue has exactly the same effect as using the "compliant" version as an r-value.
它是一个包含一个空指针常数的圆括号表达式,因此它无疑是一个空指针值。使用它作为一个rvalue与使用“兼容”版本作为r值具有完全相同的效果。
If there were some syntactic rules that could only accept a null pointer constant, it would not qualify. But I'm not aware of any (though I'm less expert on C).
如果有一些语法规则只能接受一个空指针常量,则不符合条件。但我不知道(尽管我对C不太精通)。
And while neither one is a constant (referring to the formal grammar production), both can appear in a constant expression in an initializer, because both null pointer constants and address constants are allowed, and a constant null pointer value is explicitly included in the category of address constant.
虽然两者都不是常量(引用正式的语法生成),但它们都可以出现在初始化器中的常量表达式中,因为空指针常量和地址常量都是允许的,并且常量空指针值显式包含在地址常量的类别中。
Pointer comparisons also specifically mention null pointer constants... but here pointer values are also accepted, and all null pointer values are treated equally. Same for the ternary and assignment operators.
指针比较也特别提到了空指针常量…但是这里指针值也被接受,所有空指针值都被平等对待。三元运算符和赋值运算符也是如此。
Please do be aware that these rules are quite different in C++, where both the above expressions are constant null pointer values of type void*
, but not universal null pointer constants. Null pointer constants in C++ are integral constant expressions which evaluate to zero. And void*
doesn't implicitly convert to other pointer types.
请注意,在c++中,这些规则是完全不同的,上面两个表达式都是void*类型的常量空指针值,但不是通用空指针常量。c++中的空指针常量是积分常量表达式,其值为零。void*不会隐式地转换为其他指针类型。
#1
25
Doesn't the bolded sentence contradict the author's claim that
((void*)0)
is not a null pointer constant?粗体字句子是否与作者声称(void*)0不是空指针常数的说法相矛盾?
No, it doesn't. (I confess to being a bit biased, since the referenced blog is mine.)
不,它不是。(我承认我有点偏见,因为引用的博客是我的。)
The bolded sentence says that its type and value are identical to those of the unparenthesized expression. That's not enough to imply that it's a null pointer constant.
粗体字表示它的类型和值与无括号的表达式相同。这还不足以说明它是一个空指针常数。
Consider:
考虑:
void *var = 0;
(void*)0
is a null pointer constant. ((void*)0)
has the same type and value as (void*)0
. var
also has the same type and value as (void*)0
, but var
clearly is not a null pointer constant.
0是一个空指针常数。((void*)0)具有与(void*)0相同的类型和值。var的类型和值也与(void*)0相同,但是var显然不是空指针常量。
Having said that, I'm 99+% sure that the intent is that ((void*)0)
is a null pointer constant, and more generally that any parenthesized null pointer constant is a null pointer constant. The authors of the standard merely neglected to say so. And since the description of parenthesized expressions in 6.5.1p5 specifically enumerates several other characteristics that are inherited by parenthesized expressions:
话虽如此,我还是99+%地确信(void*)0是一个空指针常量,更一般地说,任何括号内的空指针常量都是一个空指针常量。《标准》的作者们只是忽略了这一点。由于在6.5.1p5中对括号内表达式的描述特别列举了由括号内表达式继承的其他几个特征:
A parenthesized expression is a primary expression. Its type and value are identical to those of the unparenthesized expression. It is an lvalue, a function designator, or a void expression if the unparenthesized expression is, respectively, an lvalue, a function designator, or a void expression.
括号内的表达式是主表达式。它的类型和值与无括号表达式的类型和值相同。如果非插入式表达式分别是lvalue、函数指定器或空表达式,则它是lvalue、函数指定器或空表达式。
the omission is troubling (but only mildly so).
遗漏是令人不安的(但只是轻微的)。
But let's assume, for the sake of argument, that ((void*)0)
is not a null pointer constant. What difference does it make?
但我们假设(void*)0不是空指针常数。这有什么区别?
(void*)0
is a null pointer constant, whose value is a null pointer of type void*
, so by the semantics of parenthesized expressions ((void*)0)
also has a value that is a null pointer of type void*
. Both (void*)0
and ((void*)0)
are address constants. (Well, I think they are.) So what contexts require a null pointer constant and do not accept an address constant? There are only a few.
(void*)0是一个空指针常量,其值是void*类型的空指针,因此根据圆括号表达式(void*)0的语义,它的值也是void*类型的空指针。(void*)0和(void*)0都是地址常量。(嗯,我想是的。)那么,什么上下文需要一个空指针常量而不接受地址常量呢?只有几个。
6.5.9 Equality operators
An expression of function pointer type may be compared for equality to a null pointer constant. (An object pointer may be compared to an expression of type void*
, but a function pointer may not, unless it's a null pointer constant.) So this:
可以将函数指针类型的表达式与空指针常数进行比较。(对象指针可以与void*类型的表达式进行比较,但函数指针则不能,除非它是一个空指针常量。)所以这个:
void func(void);
if (func == ((void*)0)) { /* ... */ }
would be a constraint violation.
会违反约束。
6.5.16.1 Simple assignment
In an assignment, a null pointer constant may be assigned to an object of pointer-to-function type, and will be implicitly converted. An expression of type void*
that's not a null pointer constant may not be assigned to a function pointer. The same constraints apply to argument passing and initialization. So this:
在赋值中,可以将空指针常量分配给指针到函数类型的对象,并将其隐式转换。非空指针常量的void*类型的表达式可能不会分配给函数指针。同样的约束适用于参数的传递和初始化。所以这个:
void (*fp)(void) = ((void*)0);
would be a constraint violation if ((void*)0)
were not a null pointer constant. Thanks to commenter hvd for finding this.
如果(void*)0不是空指针常量,则会违反约束。感谢hvd评论者的发现。
7.19 Common definitions <stddef.h>
The macro NULL
expands to "an implementation-defined null pointer constant". If ((void*)0)
is not a null pointer constant, then this:
宏NULL扩展为“实现定义的空指针常量”。如果(void*)0不是空指针常量,则如下:
#define NULL ((void*)0)
would be invalid. This would be a restriction imposed on the implementation, not on programmers. Note that this:
将是无效的。这将是对实现的限制,而不是对程序员。注意:
#define NULL (void*)0
is definitely invalid, since macro definitions in standard headers must be fully protected by parentheses where necessary (7.1.2p5). Without the parentheses, the valid expression sizeof NULL
would be a syntax error, expanding to sizeof (void*)
followed by an extraneous constant 0
.
是绝对无效的,因为标准头中的宏定义必须在必要时由圆括号完全保护(7.1.2p5)。如果没有括号,有效的表达式sizeof NULL将是一个语法错误,扩展到sizeof (void*),然后是一个无关的常量0。
#2
6
It is a parenthesized expression which contains a null pointer constant, so it indisputably is a null pointer value. Using it as an rvalue has exactly the same effect as using the "compliant" version as an r-value.
它是一个包含一个空指针常数的圆括号表达式,因此它无疑是一个空指针值。使用它作为一个rvalue与使用“兼容”版本作为r值具有完全相同的效果。
If there were some syntactic rules that could only accept a null pointer constant, it would not qualify. But I'm not aware of any (though I'm less expert on C).
如果有一些语法规则只能接受一个空指针常量,则不符合条件。但我不知道(尽管我对C不太精通)。
And while neither one is a constant (referring to the formal grammar production), both can appear in a constant expression in an initializer, because both null pointer constants and address constants are allowed, and a constant null pointer value is explicitly included in the category of address constant.
虽然两者都不是常量(引用正式的语法生成),但它们都可以出现在初始化器中的常量表达式中,因为空指针常量和地址常量都是允许的,并且常量空指针值显式包含在地址常量的类别中。
Pointer comparisons also specifically mention null pointer constants... but here pointer values are also accepted, and all null pointer values are treated equally. Same for the ternary and assignment operators.
指针比较也特别提到了空指针常量…但是这里指针值也被接受,所有空指针值都被平等对待。三元运算符和赋值运算符也是如此。
Please do be aware that these rules are quite different in C++, where both the above expressions are constant null pointer values of type void*
, but not universal null pointer constants. Null pointer constants in C++ are integral constant expressions which evaluate to zero. And void*
doesn't implicitly convert to other pointer types.
请注意,在c++中,这些规则是完全不同的,上面两个表达式都是void*类型的常量空指针值,但不是通用空指针常量。c++中的空指针常量是积分常量表达式,其值为零。void*不会隐式地转换为其他指针类型。