(int *)0是否为空指针?

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

6.3.2.3.3中的C11标准说:

值为0的整型常量表达式或类型为void *的表达式称为空指针常量。

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

但是,标准然后继续:

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

它覆盖(int *)0作为空指针,因为强制转换是转换方法下列出的显式转换 (C11,6.3)。

然而,令我惊讶的是以下这句话

…或者这样的表达式转换为void *

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


可以帮助讨论的另一个问题如下。 是(long long)123被认为是“123转换为long long ”,或“123 long longlong long ”。 换句话说, (long long)123是否有任何转换? 如果没有,那么上面的第二个引号不会覆盖(int *)0作为空指针。

简短回答:

在C和C ++中, (int *)0是一个常量表达式,其值为空指针。 但是,它不是空指针常量 。 我知道,constant-expression-which-value-is-null-pointer和null-pointer-constant之间唯一可观察到的区别是null-pointer-constant可以赋值给任何一个左值。指针类型,但是一个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 *)0的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 in C++ assert (x == (int *)0); // must succeed in C, unspecified in 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整型常量表达式用于指定结构的位字段成员的大小,枚举常量的值,数组的大小或大小写常量的值。 适用于[ #if ]中使用的整型常量表达式的其他约束在6.10.1中讨论。

    为了讨论的目的,重要的是

    转换运算符…只能将算术类型转换为整数类型

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

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

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

值为0的整型常量表达式或类型为void *的表达式称为空指针常量 。 如果将空指针常量转换为指针类型,则保证将结果指针(称为空指针)与指向任何对象或函数的指针进行比较。

Standardese非常非常直观。 这两句话的意思完全相同:

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

(斜体 – 术语的定义。粗体 – 我的重点。)那么这意味着,在C中, (long *)0(long *)(void *)0是两种写出完全相同的东西的方式,即null类型为long *指针。

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

空指针常量是整数类型的整数常量表达式(5.19)rvalue,其值为零。

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

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

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

计算表达式(int*)0产生int*类型的空指针。

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

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

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

至于你关于(long long)123 ,我不确定它是如何相关的,但表达式123的类型是int ,而cast指定了从intlong long的转换。

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