这可以被认为是this question的扩展(我只对C感兴趣,但是添加C ++来完成扩展)
6.3.2.3.3的C11标准说:
值为0的整型常量表达式,或者类型为
void *
的表达式,称为空指针常量。
我个人对此的看法是0
和(void *)0
表示空指针,其整数值实际上可能不是0,但不包括0强制转换为任何其他类型。< / p>
但是,标准然后继续:
如果将空指针常量转换为指针类型,则生成的指针称为空指针,...
将(int *)0
作为空指针,因为强制转换是显式转换(C11,6.3),它在转换方法下列出。
然而,令我惊讶的是以下短语
...或此类表达式转换为
void *
...
有了上述语义,这句话似乎完全没用。问题是,这句话完全没用吗?如果没有,它有什么影响?因此,是否(int *)0
空指针?
可以帮助讨论的另一个问题如下。 (long long)123
被视为“123转换为long long
”或“123类型为long long
”。换句话说,(long long)123
中是否有任何转化?如果没有,那么上面的第二个引号不会覆盖(int *)0
作为空指针。
答案 0 :(得分:29)
简短回答:
在C和C ++中,(int *)0
是一个常量表达式,其值为空指针。但是,它不是空指针常量。我知道,constant-expression-which-value-is-null-pointer和null-pointer-constant之间唯一可观察到的区别是null-pointer-constant可以赋值给任何一个lvalue。指针类型,但是constant-expression-which-value-is-a-null-pointer具有特定的指针类型,并且只能分配给具有兼容类型的左值。在C中,而不是C ++,(void *)0
也是一个空指针常量;这是void *
的特殊情况,与一般的C-but-not-C ++规则一致,void *
与任何其他指针到对象类型的赋值兼容。
例如:
long *a = 0; // ok, 0 is a null pointer constant
long *b = (long *)0; // ok, (long *)0 is a null pointer with appropriate type
long *c = (void *)0; // ok in C, invalid conversion in C++
long *d = (int *)0; // invalid conversion in both C and C++
在这种情况下,空指针常量(void *)0
与类型为void *
的constant-expression-which-is-a-null指针之间的差异是可见的,即使在C:
typedef void (*fp)(void); // any pointer-to-function type will show this effect
fp a = 0; // ok, null pointer constant
fp b = (void *)0; // ok in C, invalid conversion in C++
fp c = (void *)(void *)0; // invalid conversion in both C and C++
此外,它现在没什么问题,但是自从你提出来之后:无论 long *
的空指针的位代表是什么,所有的这些断言的行为如评论所示:
// 'x' is initialized to a null pointer
long *x = 0;
// 'y' is initialized to all-bits-zero, which may or may not be the
// representation of a null pointer; moreover, it might be a "trap
// representation", UB even to access
long *y;
memset(&y, 0, sizeof y);
assert (x == 0); // must succeed
assert (x == (long *)0); // must succeed
assert (x == (void *)0); // must succeed in C, unspecified behavior in C++
assert (x == (int *)0); // invalid comparison in both C and C++
assert (memcmp(&x, &y, sizeof y) == 0); // unspecified
assert (y == 0); // UNDEFINED BEHAVIOR: y may be a trap representation
assert (y == x); // UNDEFINED BEHAVIOR: y may be a trap representation
&#34;未指定&#34;比较不会引起不确定的行为,但标准并没有说明它们是评估是真还是假,并且实现不需要记录它们中的哪一个,或者甚至选择一个并坚持它。如果您多次调用它,则上述memcmp
在返回0和1之间交替完全有效。
标准引语的长答案:
要了解空指针常量是什么,首先必须了解整数常量表达式是什么,并且它非常多毛 - 一个完整的理解要求您详细阅读C99的6.5和6.6节。这是我的总结:
常量表达式是任何C表达式,编译器可以在不知道任何对象(const
或其他值)的情况下将其计算为常量;但是,{{1} }值是公平的游戏),没有副作用。 (这是大约25页标准的大幅简化,可能不准确。)
整数常量表达式是常量表达式的受限子集,可方便地在单个段落中定义,C99 6.6p6及其脚注:
整数常量表达式 96 应具有整数类型,并且只能具有整数常量的操作数,枚举常量,字符常量,结果为整数常量的
enum
表达式,以及浮点常量这是演员的直接操作数。整数常量表达式中的转换运算符只能将算术类型转换为整数类型,除非作为sizeof
的操作数的一部分 操作96 整数常量表达式用于指定结构的位字段成员的大小,枚举常量的值,数组的大小或大小写的值不变。适用于[
sizeof
]中使用的整型常量表达式的其他约束将在6.10.1中讨论。
为了讨论的目的,重要的是
Cast运算符...只能将算术类型转换为整数类型
这意味着#if
不是 整数常量表达式,尽管它是一个常量表达式。
C ++ 98定义似乎或多或少等价,模C ++特性和与C的偏差。例如,C ++中字符和布尔类型与整数类型的强分离意味着C ++标准说的是&# 34; 积分常量表达式&#34;而不是&#34; 整数常量表达式&#34;,然后有时不仅需要一个整数常量表达式,而且需要整数类型的整数常量表达式,不包括{{ 1}},(int *)0
和char
(也许还有wchar_t
和bool
?我从文中不清楚这一点。)
现在,空指针常量的C99定义就是这个问题的全部内容,所以我将重复它:6.3.2.3p3说
一个整数常量表达式,其值为0,或者这样的表达式强制转换为类型
signed char
被称为空指针常量。如果将空指针常量转换为a 指针类型,结果指针,称为空指针,保证比较不等于指向任何对象或函数的指针。
Standardese非常非常直接。这两句话的意思完全相同:
值为0的整型常量表达式称为空指针常量 值为0的整数常量表达式(强制转换为
unsigned char
类型)也 空指针常量。
当任何空指针常量转换为指针类型时,结果指针称为空指针,并保证比较不等...
(斜体 - 术语的定义。粗体 - 我的重点。)那么这意味着,在C中,void *
和void *
是两种完全相同的写法,即空指针类型(long *)0
。
C ++与众不同。等效文本是C ++ 98 4.10 [conv.ptr]:
空指针常量是整数类型的整数常量表达式(5.19)rvalue,其计算结果为零。
这就是全部。 &#34;整数类型的整数常量表达式r值&#34;与C99&#34;&#34;整数常量表达式&#34;非常相似,但有一些东西在C中有资格而不是C ++:例如,在C中字符文字(long *)(void *)0
是一个整型常量表达式,因此是一个空指针常量,但在C ++中,不是整数类型的整数常量表达式,因此它也不是空指针常量。
更重要的是,C ++没有&#34;或者这样的表达式转换为long *
&#34;条款。这意味着'\x00'
不是 C ++中的空指针常量。它仍然是空指针,但它不与任何其他指针类型分配兼容。这与C ++的通常选择型系统一致。
C ++ 11(但不是,AFAIK,C11)修改了&#34;空指针&#34;的概念,为它们添加了一种特殊类型(void *
)和一个评估为a的新关键字空指针常量(((void *)0)
)。我不完全理解这些更改,我不会尝试解释它们,但我很确定裸nullptr_t
仍然是C ++ 11中的有效空指针常量。
答案 1 :(得分:9)
评估表达式(int*)0
会产生类型为int*
的空指针。
(int*)0
不是空指针常量。
空指针常量是一种特殊的表达式,可能出现在C源代码中。 空指针是可能在正在运行的程序中出现的值。
C和C ++(两种不同的语言)在这方面的规则略有不同。 C ++没有“或者这样的表达式强制转换为void*
”字样。但我认为这不会影响你问题的答案。
至于您关于(long long)123
的问题,我不确定它是如何相关的,但表达式123
的类型为int
,而强制转换指定了{{1}的转换转到int
。
我认为核心混淆是假设long long
中的强制转换没有指定转换,因为(int*)0
已经是空指针常量。但是空指针常量不一定是指针类型的表达式。特别是,表达式0
既是空指针常量,也是类型0
的表达式;它不是任何指针类型。术语空指针常量需要被视为一个单一概念,而不是一个短语,其含义取决于构成它的单个单词。