假设 NULL 常量为零是否安全?
Is it safe to assume that the NULL constant is zero?
Richard Reese 的 Understanding and Using C Pointers 一书说:
The null concept is an abstraction supported by the null pointer
constant. This constant may or may not be a constant zero. A C
programmer need not be concerned with their actual internal
representation.
我的问题是,因为 "this constant may or may not be a constant zero," 在我的代码中执行如下操作是否安全:
int *ptr = NULL;
// Some code which probably sets ptr to a valid memory address
if(!ptr)
{
ERROR();
}
如果 NULL 不为 0,则 if 子句有可能计算为真。
if(!ptr)
是检查 NULL 指针的安全方法。
表达式 !x
完全等同于 0 == x
。常量 0
是一个 NULL 指针常量,任何指针都可以与 NULL 指针常量进行相等性比较。
即使空指针的表示不是 "all bits 0"。
也是如此
关于 !
运算符的 C standard 第 6.5.3.3p5 节指出:
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)
.
关于指针转换的第 6.3.2.3p3 节指出:
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.
Is it safe to assume that the NULL constant is zero?
NULL
将比较等于 0
.
NULL
非常 通常是零位模式。 NULL
可能是非零位模式 - 但这些天没有看到。
OP 混合了至少 4 个东西:NULL
、空指针常量、空指针、比较 空指针 到 0。C 没有定义 NULL 常量。
NULL
NULL
is a macro "which expands to an implementation-defined null
pointer constant" C17dr § 7.19 3
空指针常量
An integer constant expression with the value 0, or such an expression
cast to type void
*, is called a null pointer constant. C17dr § §
6.3.2.3 3
因此空指针常量的类型可能是int
、unsigned
、long
, ... 或 void *
.
当整数常量表达式1时,空指针常量值为0。作为像((void *)0)
这样的指针,它的value/encoding是没有规定的。它普遍存在零的位模式,但没有指定。
可能有很多空指针常量。他们都比较相等。
注:空指针常量的大小,当为整数时,可能与对象指针的大小不同.通常通过根据需要附加一个 L
或两个后缀来避免这种大小差异。
空指针
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. C17dr § § 6.3.2.3 3
Conversion of a null pointer to another pointer type yields a null
pointer of that type. Any two null pointers shall compare equal. C17dr
§ § 6.3.2.3 4
空指针的类型是一些指针,可以是像int *, char *
这样的对象指针,也可以是像int (*)(int, int)
或void *
这样的函数指针。
未指定空指针的值。它普遍存在零的位模式,但没有指定。
所有 空指针 都比较相等,无论它们的编码如何。
比较一个空指针和0
if(!ptr)
等同于 if(!(ptr != 0))
。当指针ptr
,即空指针,与0进行比较时,零被转换为指针,空指针 ] 同类型:int *
。这2个空指针,可能有不同的位模式,比较相等。
那么什么时候假设 NULL 常量为零是不安全的呢?
NULL
可能是 ((void*)0)
并且它的位模式可能不同于零。无论其编码如何,它确实像上面那样比较等于 0。已经讨论过调用指针比较,而不是整数比较。将 NULL
转换为整数可能不会导致整数值 0,即使 ((void*)0)
全部为零位也是如此。
printf("%ju\n", (uintmax_t)(uintptr_t)NULL); // Possible not 0
注意这是将指针转换为整数,而不是 if(!ptr)
的情况,其中 0 被转换为指针。
C 规范包含许多旧的做事方式,并且对新颖的新方式持开放态度。我从来没有遇到过 NULL
不是全零位模式的实现。鉴于存在许多假设 NULL
都是零位的代码,我怀疑只有旧的模糊实现使用过非零位模式 NULL
并且 NULL
几乎可以肯定是全零位模式。
1 空指针常量 是 1) 整数或 2) 一个 void*
。 “当一个整数......”指的是第一种情况,而不是第二种情况的强制转换或转换,如 (int)((void*)0)
.
chux 写了一个很好的、详细的答案,但具体到那本书,我会怀疑它的质量:
This constant may or may not be a constant zero
这是错误的,它必须始终为零或零转换为 void*
。空指针常量的定义见C17 6.3.2.3/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.
这意味着所有整数常量表达式如0
、0L
、0u
、0x0
、'[=15=]'
等都是空指针常量。如果其中任何一个被强制转换为 void*
,它也是一个空指针常量。
A C programmer need not be concerned with their actual internal representation.
作者显然混淆了空指针常量和空指针这两个正式术语。程序员不需要关心空指针的内部表示。他们确实需要知道是什么使有效的空指针成为常量。最安全、最易读的方法是使用 NULL
宏,它保证是一个空指针常量。
所以关于你的问题 "is it safe for me to do things like the below in my code" - 是的,!ptr
检查空指针是完全安全的,即使 ptr==NULL
是更易读的代码。
Richard Reese 的 Understanding and Using C Pointers 一书说:
The null concept is an abstraction supported by the null pointer constant. This constant may or may not be a constant zero. A C programmer need not be concerned with their actual internal representation.
我的问题是,因为 "this constant may or may not be a constant zero," 在我的代码中执行如下操作是否安全:
int *ptr = NULL;
// Some code which probably sets ptr to a valid memory address
if(!ptr)
{
ERROR();
}
如果 NULL 不为 0,则 if 子句有可能计算为真。
if(!ptr)
是检查 NULL 指针的安全方法。
表达式 !x
完全等同于 0 == x
。常量 0
是一个 NULL 指针常量,任何指针都可以与 NULL 指针常量进行相等性比较。
即使空指针的表示不是 "all bits 0"。
也是如此关于 !
运算符的 C standard 第 6.5.3.3p5 节指出:
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 typeint
. The expression!E
is equivalent to(0==E)
.
关于指针转换的第 6.3.2.3p3 节指出:
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.
Is it safe to assume that the NULL constant is zero?
NULL
将比较等于 0
.
NULL
非常 通常是零位模式。 NULL
可能是非零位模式 - 但这些天没有看到。
OP 混合了至少 4 个东西:NULL
、空指针常量、空指针、比较 空指针 到 0。C 没有定义 NULL 常量。
NULL
NULL
is a macro "which expands to an implementation-defined null pointer constant" C17dr § 7.19 3
空指针常量
An integer constant expression with the value 0, or such an expression cast to type
void
*, is called a null pointer constant. C17dr § § 6.3.2.3 3
因此空指针常量的类型可能是int
、unsigned
、long
, ... 或 void *
.
当整数常量表达式1时,空指针常量值为0。作为像((void *)0)
这样的指针,它的value/encoding是没有规定的。它普遍存在零的位模式,但没有指定。
可能有很多空指针常量。他们都比较相等。
注:空指针常量的大小,当为整数时,可能与对象指针的大小不同.通常通过根据需要附加一个 L
或两个后缀来避免这种大小差异。
空指针
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. C17dr § § 6.3.2.3 3
Conversion of a null pointer to another pointer type yields a null pointer of that type. Any two null pointers shall compare equal. C17dr § § 6.3.2.3 4
空指针的类型是一些指针,可以是像int *, char *
这样的对象指针,也可以是像int (*)(int, int)
或void *
这样的函数指针。
未指定空指针的值。它普遍存在零的位模式,但没有指定。
所有 空指针 都比较相等,无论它们的编码如何。
比较一个空指针和0
if(!ptr)
等同于 if(!(ptr != 0))
。当指针ptr
,即空指针,与0进行比较时,零被转换为指针,空指针 ] 同类型:int *
。这2个空指针,可能有不同的位模式,比较相等。
那么什么时候假设 NULL 常量为零是不安全的呢?
NULL
可能是 ((void*)0)
并且它的位模式可能不同于零。无论其编码如何,它确实像上面那样比较等于 0。已经讨论过调用指针比较,而不是整数比较。将 NULL
转换为整数可能不会导致整数值 0,即使 ((void*)0)
全部为零位也是如此。
printf("%ju\n", (uintmax_t)(uintptr_t)NULL); // Possible not 0
注意这是将指针转换为整数,而不是 if(!ptr)
的情况,其中 0 被转换为指针。
C 规范包含许多旧的做事方式,并且对新颖的新方式持开放态度。我从来没有遇到过 NULL
不是全零位模式的实现。鉴于存在许多假设 NULL
都是零位的代码,我怀疑只有旧的模糊实现使用过非零位模式 NULL
并且 NULL
几乎可以肯定是全零位模式。
1 空指针常量 是 1) 整数或 2) 一个 void*
。 “当一个整数......”指的是第一种情况,而不是第二种情况的强制转换或转换,如 (int)((void*)0)
.
chux 写了一个很好的、详细的答案,但具体到那本书,我会怀疑它的质量:
This constant may or may not be a constant zero
这是错误的,它必须始终为零或零转换为
void*
。空指针常量的定义见C17 6.3.2.3/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.
这意味着所有整数常量表达式如
0
、0L
、0u
、0x0
、'[=15=]'
等都是空指针常量。如果其中任何一个被强制转换为void*
,它也是一个空指针常量。A C programmer need not be concerned with their actual internal representation.
作者显然混淆了空指针常量和空指针这两个正式术语。程序员不需要关心空指针的内部表示。他们确实需要知道是什么使有效的空指针成为常量。最安全、最易读的方法是使用
NULL
宏,它保证是一个空指针常量。
所以关于你的问题 "is it safe for me to do things like the below in my code" - 是的,!ptr
检查空指针是完全安全的,即使 ptr==NULL
是更易读的代码。