当空指针不是所有位都为零时,如何正确编写 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*)0
。 NULL
不能是别的。
但是,当将空指针常量分配给指针时,您会得到一个 空指针,它的值可能不是零,即使它比较等于空指针常量。代码 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。
正如 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*)0
。 NULL
不能是别的。
但是,当将空指针常量分配给指针时,您会得到一个 空指针,它的值可能不是零,即使它比较等于空指针常量。代码 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。