理查德·里斯(Richard Reese)的《理解和使用C指针》一书说:
空概念是空指针支持的抽象 不断。此常量可能是也可能不是常数零。一 C 程序员不需要关心他们的实际内部 表示法。
我的问题是,既然"这个常数可能是也可能不是常数零",那么在我的代码中做下面这样的事情对我来说是否安全:
int *ptr = NULL;
// Some code which probably sets ptr to a valid memory address
if(!ptr)
{
ERROR();
}
如果 NULL 不为 0,则 if 子句的计算结果有可能为 true。
假设 NULL 常量为零是否安全?
NULL
将比较等于0
.NULL
通常是零位模式。NULL
可能是非零位模式 - 但如今看不到。
OP 混合了至少 4 件事:NULL
、空指针常量、空指针、将空指针与 0 进行比较。 C 不定义NULL 常量。
NULL
NULL
是一个宏"它扩展到实现定义的空 指针常量" C17dr § 7.19 3
空指针常量
值为 0 的整数常量表达式或此类表达式 强制转换为类型
void
*,称为空指针常量。C17dr § § § 6.3.2.3 3
因此,空指针常量的类型可以是int
、unsigned
、long
、...或void *
.
当整数常量表达式1时,空指针常量值为 0。 作为像((void *)0)
这样的指针,它的值/编码没有指定。 它普遍具有零的位模式,但没有指定。
可能有许多空指针常量。 它们彼此比较相等。
注意:当空指针常量为整数时,它的大小可能与对象指针的大小不同。 通常通过根据需要附加一两个L
后缀来避免这种大小差异。
空指针
如果将空指针常量转换为指针类型,则 生成的指针(称为空指针)保证可以比较 不等于指向任何对象或函数的指针。 C17dr § § 6.3.2.3 3
将空指针转换为另一种指针类型将生成空 该类型的指针。任何两个空指针的比较应相等。C17dr § § 6.3.2.3 4
空指针的类型是一些指针,可以是对象指针(如int *, char *
)或函数指针(如int (*)(int, int)
或void *
)。
未指定空指针的值。 它普遍具有零的位模式,但没有指定。
所有空指针都相等,无论其编码如何。
将空指针与 0 进行比较
if(!ptr)
与if(!(ptr != 0))
相同。 当指针ptr
(一个空指针)与 0 进行比较时,零将转换为指针,即相同类型的空指针:int *
。 这两个空指针可能具有不同的位模式,比较相等。
那么,当假设 NULL 常量为零是不安全的吗?
NULL
可能是((void*)0)
,其位模式可能与零不同。 无论其编码如何,它都会像上面一样比较等于 0。 已经讨论了召回指针比较,而不是整数比较。将NULL
转换为整数可能不会导致整数值为 0,即使((void*)0)
都是零位。
printf("%jun", (uintmax_t)(uintptr_t)NULL); // Possible not 0
请注意,这是将指针转换为整数,而不是将 0 转换为指针的if(!ptr)
的情况。
C 规范包含许多旧的做事方式,并对新颖的新方式持开放态度。 我从未遇到过NULL
不是全零位模式的实现。 鉴于存在许多假设NULL
都是零位的代码,我怀疑只有旧的晦涩实现曾经使用过非零位模式NULL
并且NULL
几乎可以肯定是全零位模式。
1空指针常量为 1) 整数或 2)void*
。 "当整数..."是指第一种情况,而不是像(int)((void*)0)
那样对第二种情况进行铸造或转换。
if(!ptr)
是检查 NULL 指针的安全方法。
表达式!x
完全等效于0 == x
。 常量0
是 NULL 指针常量,可以将任何指针与NULL 指针常量进行比较以使其相等。
即使空指针的表示形式不是"所有位 0",也是如此。
C 标准关于!
运算符的第 6.5.3.3p5 节规定:
逻辑否定运算符
!
的结果为 0,如果 其操作数的值比较不等于 0,如果其值为 1 操作数比较等于 0。 结果的类型为int
。这 表达式!E
等效于(0==E)
。
关于指针转换的第 6.3.2.3p3 节指出:
值为 0 的整数常量表达式,或类似的 转换为类型
void *
的表达式称为空指针 常数。如果将空指针常量转换为指针类型, 生成的指针(称为null 指针)保证可以比较 不等于指向任何对象或函数的指针。
Chux 写了一个很好的、详细的答案,但具体到那本书,我对它的质量持怀疑态度:
-
此常量可能是也可能不是常数零
这是错误的,它必须始终是零或零投到
void*
.空指针常量的定义可在 C17 6.3.2.3/3 中找到:值为 0 的整数常量表达式,或强制转换为类型的此类表达式 void * 称为空指针常量。如果将空指针常量转换为 指针类型,生成的指针,称为空指针,保证比较不相等 指向任何对象或函数的指针。
这意味着所有整数常量表达式如
0
、0L
、0u
、0x0
、' '
等都是空指针常量。如果它们中的任何一个被强制转换为void*
,它也是一个空指针常量。 -
C程序员不需要关心他们实际的内部表示。
作者显然混淆了空指针常量和空指针这两个形式术语。程序员不需要关心空指针的内部表示。不过,他们确实需要知道是什么使有效的空指针常量。最安全、最易读的方法是使用
NULL
宏,它保证为 null 指针常量。
因此,关于您的问题"在我的代码中执行如下所示的操作对我来说是否安全" - 是的,即使ptr==NULL
代码更具可读性,也完全安全地执行!ptr
检查空指针。