【问题标题】:Is it safe to assume that the NULL constant is zero?假设 NULL 常量为零是否安全?
【发布时间】:2020-07-01 05:55:37
【问题描述】:

Richard Reese 所著的理解和使用 C 指针一书说:

空概念是空指针支持的抽象 持续的。该常数可能是也可能不是常数零。交流电 程序员不需要关心他们实际的内部 表示。

我的问题是,由于“这个常数可能是常数零,也可能不是常数零”,我在我的代码中执行以下操作是否安全:

int *ptr = NULL;
// Some code which probably sets ptr to a valid memory address

if(!ptr)
{
   ERROR();
}

如果 NULL 不为 0,则 if 子句有可能评估为 true。

【问题讨论】:

标签: c pointers null null-pointer


【解决方案1】:

假设 NULL 常量为零是否安全?

NULL 将与 0 比较。
NULL 通常是 非常 零位模式。 NULL 可能是一个非零位模式 - 但这些天没有出现。


OP 混合了至少 4 个东西:NULL空指针常量空指针,将 空指针 与 0 进行比较。 C 没有定义 NULL 常量

NULL

NULL 是一个宏“扩展为实现定义的 null 指针常量”C17dr § 7.19 3

空指针常量

值为 0 的整数常量表达式,或这样的表达式 转换为void * 类型,称为空指针常量。 C17dr § § 6.3.2.3 3

因此,空指针常量类型可能是intunsignedlong、...或void *

当整数常量表达式1时,空指针常量 为0。像((void *)0)这样的指针,它的值/未指定编码。它普遍存在零位模式,但未指定。

可能有很多空指针常量。它们都相互比较。

注意:空指针常量大小,当它是整数时,可能与对象指针的大小不同。通常可以通过附加L 或根据需要添加两个后缀来避免这种大小差异。

空指针

如果将空指针常量转换为指针类型,则 结果指针,称为空指针,保证比较 不等于指向任何对象或函数的指针。 C17dr § § 6.3.2.3 3

将空指针转换为另一种指针类型会产生空指针 该类型的指针。任何两个空指针应该比较相等。 C17博士 § 6.3.2.3 4

空指针的类型是一些指针,要么是像int *, char *这样的对象指针,要么是像int (*)(int, int)void *这样的函数指针。

空指针没有被指定。它普遍存在零位模式,但未指定。

所有空指针比较相等,不管它们的编码。

比较一个空指针和0

if(!ptr)if(!(ptr != 0)) 相同。当指针ptr,它是一个空指针,与0进行比较时,零被转换成一个指针,一个相同类型的空指针:@987654342 @。这 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) 中第二种情况的强制转换或转换。

【讨论】:

  • @EricPostpischil 回复:对此并不肯定,“值为 0 的整数常量表达式,...,被称为空指针常量”--> 稍微重新措辞了我对该案例的回答当空指针常量为整数时。在我看来很清楚,当整数值为零时。
  • @EricPostpischil 回复:“(int) NULL 不一定是零”--> 同意答案是“NULL 可能是 ((void*)0),并且它的位模式可能与零不同"。
  • “当整数时,空指针常量值为0”:这个还是有问题的。该标准没有提供任何理由假设,当一个值为 0 的整数常量用于源代码上下文时,要求将其解释为空指针常量,它将被转换为一个指针值,其表示形式为所有位为零。我不清楚这就是您要断言的内容,但这也是该陈述有问题的原因。
  • @JohnBollinger 同意“没有理由假设……值为 0 的整数常量……要求将其解释为空指针常量”。我的断言是相反的,当 空指针常量 是某个整数时,它的值为 0。不是整数零是 空指针常量
  • @EricPostpischil 添加注释以回答关于解释的第一部分。 implementation might use 0xffffffff 包含在答案的“所以当假设.. 不安全时”部分。
【解决方案2】:

if(!ptr) 是一种检查 NULL 指针的安全方法。

表达式!x 完全等价于0 == x。常量0 是一个NULL 指针常量,任何指针都可以与一个NULL 指针常量进行比较是否相等。

即使空指针的表示不是“所有位 0”,这也是正确的。

C standard 中关于 ! 运算符的第 6.5.3.3p5 节指出:

逻辑否定运算符! 的结果为 0,如果 其操作数的值比较不等于 0,如果其操作数的值比较不等于 1 操作数比较等于 0。结果的类型为 int。这 表达式!E 等价于(0==E)

以及关于指针转换状态的第 6.3.2.3p3 节:

值为 0 的整数常量表达式,或者这样的 转换为void * 类型的表达式称为空指针 常数。如果将空指针常量转换为指针类型, 生成的指针,称为 null 指针,保证可以比较 不等于指向任何对象或函数的指针。

【讨论】:

    【解决方案3】:

    chux 写了一个很好的详细答案,但具体到那本书,我会怀疑它的质量:

    • 这个常数可能是也可能不是常数零

      这是错误的,它必须始终是零或零转换为void*。空指针常量的定义见C17 6.3.2.3/3:

      值为 0 的整数常量表达式,或这种类型的表达式 void *,称为空指针常量。如果将空指针常量转换为 指针类型,生成的指针,称为空指针,保证比较不相等 指向任何对象或函数的指针。

      这意味着所有整数常量表达式,如00L0u0x0'\0' 等都是空指针常量。如果其中任何一个被强制转换为void*,它也是一个空指针常量。

    • C 程序员不必关心他们实际的内部表示。

      作者显然是混淆了空指针常量空指针这两个正式术语。程序员不需要关心空指针的内部表示。他们确实需要知道是什么使一个有效的空指针成为常量。最安全、最易读的方法是使用NULL 宏,它保证为空指针常量。

    所以关于你的问题“我在我的代码中执行以下操作对我来说是否安全” - 是的,执行!ptr 检查空指针是完全安全的,即使ptr==NULL 是更具可读性的代码.

    【讨论】:

    • 用“常量可能是也可能不是常量零”断言错误的一个微妙弱点是 zero 是一个数字,而 (void*)0 是一个指针 - 并且因为指针可以具有与0 不同的大小和编码。 - 。作为整数,零必须全为 0 位。 (暂时将古老的非 2 的补码与它的 -0 放在一边)。指针(void*)0 可能具有非零位模式。 IAC,空指针常量,作为整数或指针,比较等于0,与编码/大小无关。
    • @chux-ReinstateMonica (void*)0 不是 常量0 是。
    • 同意0 是一个常量,一个整数常量。我会说(void*)0 充其量是一个“固定地址”。 IAC,空指针常量可以是(void*)0
    猜你喜欢
    • 2020-12-15
    • 2016-12-13
    • 1970-01-01
    • 2010-09-19
    • 2013-08-23
    • 1970-01-01
    • 1970-01-01
    • 2017-02-26
    • 2011-02-27
    相关资源
    最近更新 更多