【问题标题】:Is NULL always zero in C?NULL 在 C 中总是为零吗?
【发布时间】:2012-04-11 05:57:54
【问题描述】:

我昨天面试了一个中级软件工程职位的人,他提到在 C 语言中,NULL 并不总是零,并且他看到了 C 中 NULL 不为零的实现。我觉得这很可疑,但我想确定一下。有谁知道他是对的吗?

(回复不会影响我对该候选人的判断,我已将决定提交给我的经理。)

【问题讨论】:

标签: c pointers null zero


【解决方案1】:

我假设你的意思是空指针。保证比较等于01 但它不必用全零位表示。2

另请参阅关于空指针的comp.lang.c FAQ


  1. 参见 C99,6.3.2.3。
  2. 没有明确的声明;但请参阅 C99、7.20.3 的脚注(感谢 cmets 中的@birryree)。

【讨论】:

  • 作为 C99 参考的附录,因为 NULL 总是比较等于 0(空指针常量),第 7.20.3.2 节还指出所有位都设置为零(例如大小写for calloc) 不一定与0.0f 或整数0 的表示相同。
  • 太棒了。因此,如今这是很常见的做法,但不是必需的。谢谢。
  • 所以如果我将一个 NULL 指针转换为一个 int,然后打印它,我不会打印 '0'?这是否意味着编译器必须记住它何时进行指针比较,以便它可以使 NULL 指针比较等于零?
  • @chi42:标准没有说明将指针(任何指针)转换为 int 时会发生什么,除了它是实现定义的。再次参见 6.3.2.3。
  • NULL如何等于0,其位表示全为零(对吗?),但仍然等于0?
【解决方案2】:

C99 标准第 6.3.2.3 节说

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

§ 7.17 还说

[...] NULL 扩展为实现定义的空指针常量 [...]

NULL 指针的地址可能与 0 不同,但它的行为与大多数情况下相同。

(这应该与旧 C 标准中的相同,我现在手头没有)

【讨论】:

    【解决方案3】:

    空指针 constant 始终为 0。NULL 宏可由实现定义为裸 0,或像 (void *) 0 这样的强制转换表达式,或其他一些零 -值整数表达式(因此标准中的“实现定义”语言)。

    空指针可能不是0。当遇到空指针常量时,它将被转换为正确的空指针值。

    【讨论】:

    • 给我涂上愚蠢的颜色,但我担心我现在需要更多的教育。当您有指针ptr 时,ptr==NULLptr==0!ptr完全 相同吗?这个实现是否依赖?
    • @MrLister:就您的源代码而言,您是绝对正确的——ptr == 0ptr == NULL!ptr 都是等价的。然而,一旦源代码被翻译成机器代码,实际的空指针值可能不是 0(所有这些比较都将针对那个实际的空指针值)。
    • The null pointer value may be something other than 0 - 你的意思是*ptr == something 即使void *ptr = 0
    • @AlexanderSupertramp:我的意思是,程序执行所在的运行时环境可能使用0以外的值来表示“空”指针(即定义的无效地址值)。在某些系统上,0 可能是您的程序可以访问的完全有效的内存地址,并且可以为 "null" 使用不同的值(例如,0xFFFFFFFF)。但是,在您的源代码中,始终使用 0 值表达式来表示 NULL 指针常量。
    • 只是一个菜鸟问题:只是想知道当我说“null 指针的值等于该内存位置是任意的内存位置中包含的值时我是否正确选择(只是猜测)或由编译器在运行时选择,当我们编写char *p = NULL; 时,p 指向这个内存位置。”。请纠正我。
    【解决方案4】:

    在 C 语言中,只有一个上下文,必须将空指针常量显式转换为特定指针类型才能使程序正确运行。该上下文通过无类型函数参数列表传递空指针。在 modern C 中,只有当您需要将空指针传递给接受可变数量参数的函数时,才会发生这种情况。 (在传统 C 中,任何未使用原型声明的函数都会发生这种情况。)典型示例是 execl,其中最后一个参数必须是显式转换为 (char *) 的空指针:

    execl("/bin/ls", "ls", "-l", (char *)0);    // correct
    execl("/bin/ls", "ls", "-l", (char *)NULL); // correct, but unnecessarily verbose
    
    execl("/bin/ls", "ls", "-l", 0);            // undefined behavior
    execl("/bin/ls", "ls", "-l", NULL);         // ALSO undefined behavior
    

    是的,最后一个示例具有未定义的行为即使NULL 被定义为((void *)0),因为void *char * 在以下情况下是隐式可相互转换的通过无类型的参数列表传递,即使它们在其他任何地方。 (C2011 中有一种语言使它们在通过 va_arg 传递时可以隐式地相互转换,但是他们忘记指定实现提供的库函数就像通过调用 va_arg 来访问可变参数,所以你只能依赖于可变参数作为程序一部分的函数。可能应该有人提交 DR。)

    “幕后”,这里的问题只是用于空指针的位模式,而是编译器可能需要知道每个参数的确切具体类型才能正确设置调用框架。 (考虑 MC68000,它具有单独的地址和数据寄存器;一些 ABI 指定要在地址寄存器中传递的指针参数,但在数据寄存器中传递整数参数。还要考虑 intvoid * 大小不同的任何 ABI。现在它已经很少见了,但 C 仍然明确规定 void *char * 大小不同。 [编辑:我不确定,但这可能不再被允许。] ) 如果有函数原型,编译器可以使用它,但非原型函数和可变参数不能提供这样的帮助。

    C++ 比较复杂,我觉得没有资格解释。

    【讨论】:

      【解决方案5】:

      在某些实现中,指针的大小与整数的大小不同。整数上下文中的 NULL 为 0,但实际的二进制布局不必全为 0。

      【讨论】:

        猜你喜欢
        • 2010-10-02
        • 1970-01-01
        • 1970-01-01
        • 2014-11-15
        • 1970-01-01
        • 2021-03-19
        • 1970-01-01
        • 2017-05-14
        • 1970-01-01
        相关资源
        最近更新 更多