20

这可以被认为是这个问题的扩展(我只对 C 感兴趣,但添加 C++ 来完成扩展)

6.3.2.3.3 的 C11 标准说:

值为 0 的整数常量表达式,或转换为 type 的此类表达式void *称为空指针常量。

我个人对此的看法是,它0表示(void *)0空指针,其整数值实际上可能不是 0,但这不包括将 0 强制转换为任何其他类型。

但是,标准继续:

如果将空指针常量转换为指针类型,则生成的指针,称为空指针,...

它涵盖(int *)0为空指针,因为 cast 是在转换方法下列出的显式转换(C11,6.3)。

但是,仍然让我想知道的是以下短语

...或这样的表达式转换为类型void *...

有了上述语义,这句话​​似乎完全没用。问题是,这句话完全没用吗?如果不是,它有什么影响?因此,是否(int *)0是空指针?


另一个可以帮助讨论的问题如下。被(long long)123认为是“123 转换为long long”,或“123 带有类型long long”。换句话说,是否有任何转换(long long)123?如果没有,则上面的第二个引号包含(int *)0空指针。

4

2 回答 2

32

简短的回答:

在 C 和 C++ 中,(int *)0是一个常量表达式,其值为空指针。但是,它不是空指针常量。据我所知,其值为空指针的常量表达式和空指针常量之间唯一可观察到的区别是空指针常量可以分配给任何指针类型,但其值是空指针的常量表达式具有特定的指针类型,并且只能分配给具有兼容类型的左值。在 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在这种情况下,即使在 C 语言中,空指针常量和其值是空指针的常量表达式之间的差异void *也是可见的:

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

“未指定”的比较不会引发未定义的行为,但标准并没有说明它们是评估真还是假,并且实现不需要记录两者中的哪一个,甚至不需要选择一个并坚持下去。memcmp如果您多次调用它,以上内容在返回 0 和 1 之间交替是完全有效的。


带有标准引号的长答案:

要理解空指针常量是什么,首先必须理解整数常量表达式是什么,这很麻烦——要完全理解需要详细阅读 C99 的 6.5 和 6.6 节。这是我的总结:

  • 常量表达式是编译器可以在不知道任何对象的值的情况下计算为常量的任何 C 表达式(或const其他;但是,enum值是公平的游戏),并且没有副作用。(这是对大约 25 页标准语的大幅简化,可能并不准确。)

  • 整数常量表达式是常量表达式的受限子集,方便地在单个段落 C99 6.6p6 及其脚注中定义:

    整数常量表达式96应具有整数类型,并且应仅具有整数常量、枚举常量、字符常量、sizeof结果为整数常量的表达式和作为强制转换的直接操作数的浮点常量的操作数。整数常量表达式中的强制转换运算符只能将算术类型转换为整数类型,但作为运算sizeof 符操作数的一部分除外。

    96整数常量表达式用于指定结构的位域成员的大小、枚举常量的值、数组的大小或 case 常量的值。适用于 [ #if] 中使用的整数常量表达式的进一步约束在 6.10.1 中讨论。

    出于本次讨论的目的,重要的一点是

    强制转换运算符 ... 只能将算术类型转换为整数类型

    这意味着它(int *)0不是一个整数常量表达式,尽管它是一个常量表达式。

C++98 的定义似乎或多或少是等价的,模 C++ 特性和与 C 的偏差。例如,C++ 中字符和布尔类型与整数类型的更强分离意味着 C++ 标准谈到“整数常量表达式”而不是“整数常量表达式”,然后有时不仅需要一个整数常量表达式,还需要一个整数类型的整数常量表达式,不包括char, wchar_t, and bool(也许还有signed charand unsigned char? 从文本中我不清楚)。

现在,空指针常量的 C99 定义就是这个问题的全部内容,所以我会重复一遍:6.3.2.3p3 说

值为 0 的整数常量表达式,或转换为 type 的此类表达式 void *称为空指针常量。如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证不等于指向任何对象或函数的指针。

标准语非常非常字面。这两句话的意思完全一样:

值为 0 的整数常量表达式称为空指针常量
一个值为 0 的整数常量表达式,强制转换为 type void *也是一个空指针常量
任何空指针常量转换为指针类型时,生成的指针称为空指针,并保证比较不等...

(斜体 - 术语的定义。粗体 - 我的重点。)所以这意味着,在 C 中,(long *)0并且(long *)(void *)0是两种编写完全相同的东西的方式,即带有 type 的空指针long *

C++ 是不同的。等效文本是 C++98 4.10 [conv.ptr]:

空指针常量是整数类型的整数常量表达式 (5.19) 右值,其计算结果为零。

就这样。“整数类型的整型常量表达式右值”与 C99 的“整型常量表达式”几乎相同,但有一些东西在 C 中符合条件但在 C++ 中不符合条件:例如,在 C 中,字符文字'\x00'是一个整型常量表达式,因此是空指针常量,但在 C++ 中它不是整数类型的整型常量表达式,因此它也不是空指针常量。

更重要的是,C++ 没有“或这样的表达式转换为void *”子句。这意味着这((void *)0)不是C++的空指针常量。它仍然是一个空指针,但它与任何其他指针类型的赋值都不兼容。这与 C++ 通常比较挑剔的类型系统是一致的。

C++11(但不是,AFAIK,C11)修改了“空指针”的概念,为它们添加了一个特殊类型(nullptr_t)和一个新的关键字,它计算为一个空指针常量(nullptr)。我不完全理解这些变化,也不会试图解释它们,但我很确定0在 C++11 中裸露仍然是一个有效的空指针常量。

于 2014-01-27T17:12:09.567 回答
10

计算表达式会(int*)0产生一个类型为 的空指针int*

(int*)0不是空指针常量

空指针常量是可能出现在 C 源代码中的一种特殊类型的表达式。空指针是可能出现在正在运行的程序中的值。

C 和 C++(作为两种不同的语言)在这方面的规则略有不同。C++ 没有“或这样的表达式转换为类型void*”的措辞。但我认为这不会影响对您问题的回答。

至于你关于 的问题(long long)123,我不确定它是如何相关的,但表达式123是 type int,并且 cast 指定了从intto的转换long long

我认为核心混淆是一个假设,即强制(int*)0转换没有指定转换,因为0已经是一个空指针常量。但是空指针常量不一定是指针类型的表达式。特别是,该表达式0既是一个空指针常量,又是一个类型为 的表达式int;它不是任何指针类型。术语空指针常量需要被认为是一个单一的概念,而不是一个短语,其含义取决于组成它的各个单词。

于 2014-01-27T17:10:12.997 回答