当空指针不是所有位都为零时,如何正确编写 C/C++ 代码

How to write C/C++ code correctly when null pointer is not all bits zero

正如 comp.lang.c FAQ 所说,在某些体系结构中,空指针并非所有位都为零。所以问题是什么实际检查以下结构:

void* p = get_some_pointer();
if (!p)
    return;

我是将 p 与机器相关的空指针进行比较,还是将 p 与算术零进行比较?

我应该写

void* p = get_some_pointer();
if (NULL == p)
    return;

而不是为这样的架构做好准备,还是这只是我的偏执狂?

空指针在实际机器中是否全为零并不重要。假设 p 是一个指针:

if (!p) 

始终是测试 p 是否为空指针的合法方法,它始终等同于:

if (p == NULL)

您可能对另一篇 C-FAQ 文章感兴趣:This is strange. NULL is guaranteed to be 0, but the null pointer is not?


以上对于 C 和 C++ 都是正确的。请注意,在 C++(11) 中,最好对空指针文字使用 nullptr

此答案适用于 C。

不要混淆 NULL 和空指针。 NULL 只是一个保证是 空指针常量 的宏。空指针常量保证为 0(void*)0

从 C11 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 66). 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.

66) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.19.

7.19:

The macros are

NULL

which expands to an implementation-defined null pointer constant;

NULL 的情况下,实现定义是 0(void*)0NULL 不能是别的。

但是,当将空指针常量分配给指针时,您会得到一个 空指针,它的值可能不是零,即使它比较等于空指针常量。代码 if (!p)NULL 宏无关,您正在将空指针与算术值零进行比较。

所以理论上,像int* p = NULL这样的代码可能会产生一个不同于零的空指针p

根据 C 规范:

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. 55) 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.

所以0是一个空指针常量。如果我们将它转​​换为指针类型,我们将得到一个空指针,对于某些体系结构来说,它可能不是全零位。接下来让我们看看规范中关于比较指针和空指针常量的内容:

If one operand is a pointer and the other is a null pointer constant, the null pointer constant is converted to the type of the pointer.

让我们考虑(p == 0):首先0转换为空指针,然后p与空指针常量进行比较,空指针常量的实际位值取决于体系结构。

接下来,看看规范中关于否定运算符的内容:

The result of the logical negation operator ! is 0 if the value of its operand compares unequal to 0, 1 if the value of its operand compares equal to 0. The result has type int. The expression !E is equivalent to (0==E).

这意味着 (!p) 等同于 (p == 0),根据规范,p 是针对机器定义的空指针常量进行测试的。

因此,即使在空指针常量不是全零的体系结构上,您也可以安全地编写 if (!p)

对于C++,空指针常量定义为:

A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to 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.

这接近于我们为 C 所拥有的,加上 nullptr 语法糖。运算符 == 的行为定义为:

In addition, pointers to members can be compared, or a pointer to member and a null pointer constant. Pointer to member conversions (4.11) and qualification conversions (4.4) are performed to bring them to a common type. If one operand is a null pointer constant, the common type is the type of the other operand. Otherwise, the common type is a pointer to member type similar (4.4) to the type of one of the operands, with a cv-qualification signature (4.4) that is the union of the cv-qualification signatures of the operand types. [ Note: this implies that any pointer to member can be compared to a null pointer constant. — end note ]

这导致 0 转换为指针类型(对于 C)。对于否定运算符:

The operand of the logical negation operator ! is contextually converted to bool (Clause 4); its value is true if the converted operand is true and false otherwise. The type of the result is bool.

这意味着 !p 的结果取决于如何执行从指针到 bool 的转换。标准说:

A zero value, null pointer value, or null member pointer value is converted to false;

所以 if (p==NULL)if (!p) 在 C++ 中也做同样的事情。

过去,STRATUS 计算机在所有语言中的空指针都是 1。

这给 C 带来了问题,因此他们的 C 编译器允许将指针 0 和 1 与 return true

进行比较

这将允许:

void * ptr=some_func();
if (!ptr)
{
    return;
}

到空指针上的 return 即使您可以看到 ptr 在调试器中的值为 1

if ((void *)0 == (void *)1)
{
    printf("Welcome to STRATUS\n");
}

实际上会打印 "Welcome to STRATUS"

如果您的编译器足够好,有两点(而且只有两点)需要注意。

1: 静态默认初始化(即未分配)指针不会有 NULL。

2: 结构或数组上的 memset() 或扩展 calloc() 不会将指针设置为 NULL。