c++中0值的特殊地位是什么?

What is the special status of the value 0 in c++?

这纯粹是一个哲学问题。我假设没有合理的上下文证明结果是有用的(给定 nullptr)。

根据这个-https://en.cppreference.com/w/cpp/language/integer_literal,整数文字的类型是intlong intlong long intunsigned intunsigned long intunsigned long long int,如果文字的值不符合上述任何一项,则可能存在特定于实现的异常。 None 这些类型可转换为 void *,除非文字的值为 0。

不同的编译器处理方式不同。 例如,考虑以下转换:

void g(void * p){}

void f(){
    int i = 0;
    void * p;
    // p = i; // Fails. Also for other integral types.

    p = 0; // Works. Also for 00, 0x0 and 0b0. Also when adding `u` and `l` suffixes.
    g(0); // Also works.    
    // g(1); // Fails.  

    // Amazingly, even this seems to work with gcc, icc and msvc, but not with clang:
    void * x = static_cast<int>(0);
    // These works for icc and msvc, but fails with gcc and clang
    p = static_cast<int>(0);
    g(static_cast<int>(0));
}

发生了什么 "under the hood" 使编译器能够执行这些 int->void * 转换?


编辑: 具体来说,问题是标准对此有何规定?

The question is, why is this permitted according to the standard

因为需要有一种方法来表达空指针。 C 语言的设计者选择 0 为空。 C++的设计者选择了兼容C,所以0是一个空指针常量。

后来在 C++11 中,nullptr 作为新关键字引入。不可替换整型空指针常量,因为那样会破坏向后兼容性,因此这些表示空的不同方式并存。如果您不需要支持 C++11 之前的系统,则没有理由将 0 用作空指针。

and specifically what is permitted

标准说(最新草案):

[conv.ptr] A null pointer constant is an integer literal ([lex.icon]) with value zero or a prvalue of type std​::​nullptr_­t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type ([basic.compound]) and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion ([conv.qual]). A null pointer constant of integral type can be converted to a prvalue of type std​::​nullptr_­t. [ Note: The resulting prvalue is not a null pointer value. — end note ]


What happens "under the hood" that enables compilers to perform these int->void * conversions?

编译器解析源代码。语法说 0 是文字。编译器将 is 视为文字 0,因此可以按照标准将其转换为任何指针类型。


// Amazingly, even this seems to work with gcc, icc and msvc, but not with clang:
void * x = static_cast<int>(0);

自 C++11 以来,这是格式错误的。当一个格式错误的程序编译时,通常是因为

  1. 它是一个语言扩展或者
  2. 这是编译器错误或
  3. 它在旧版本的语言中格式正确,编译器的目标是

在这种情况下,它可能是语言扩展。

// These works for icc and msvc, but fails with gcc and clang
p = static_cast<int>(0);
g(static_cast<int>(0));

自 C++11 以来,这些也是格式错误的。我对 icc 和 msvc 的了解还不够,无法告诉您这些情况是否是故意的。我建议查看他们的文档。

What happens "under the hood" that enables compilers to perform these int->void * conversions?

魔术(如果您愿意,也可以只是专用规则)。字面 0(以及 0u0l 和其他变体)是特殊的。只有当这些确切的标记出现在源代码中时,编译器才会认为到指针的转换是有效的。不是0的值,是token

[conv.ptr] (emphasis mine)

1 A null pointer constant is an integer literal with value zero or a prvalue of type std​::​nullptr_­t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion.

一个 "integer literal" 具有如 [lex.icon] 中所述的精确含义。它是可以出现在我们程序源代码中的标记规范。上面引用的段落告诉我们哪些标记具有特殊含义。


我怀疑您看到的行为差异是由于 C 编程语言造成的。根据编译器前端的实现方式,它可能是此错误的来源。在C语言中,任何值为0的整型常量表达式都是空指针常量。

6.3.2.3 Pointers

3 An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

在 C++ 中也是如此,直到 CWG Issue 903 被解析为我们今天在 C++ 中拥有的规则。

如果某些编译器混合了他们的 C 和 C++ 逻辑,或者没有抓住缺陷报告的解决方案,它会解释您的观察结果。