C中的NULL总是为零吗?

chi*_*i42 56 c null pointers zero

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

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

Oli*_*rth 57

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

另请参见有关空指针的comp.lang.c常见问题解答.


  1. 见C99,6.3.2.3.
  2. 没有明确的主张; 但请参阅C99,7.20.3的脚注(感谢评论中的@birryree).

  • 作为C99引用的附录,由于"NULL"总是比较等于"0"(空指针常量),因此第7.20.3.2节还指出所有设置为零的位(例如`calloc`的情况)不一定与"0.0f"或整数"0"的表示相同. (5认同)
  • @ chi42:标准没有说明将指针(任何指针)转换为int时发生的事情,而不是它的实现定义.再次参见6.3.2.3. (5认同)

Joh*_*ode 13

空指针常量始终为0. NULL宏可以由实现定义为裸0,或类似的转换表达式(void *) 0,或者某些其他零值整数表达式(因此标准中的"实现定义"语言).

空指针可以是0以外的值.当遇到空指针常量时,它将被转换为正确的空指针值.

  • @MrLister:在你的*源代码*的上下文中,你是绝对正确的 - `ptr == 0`和`ptr == NULL`和`!ptr`都是等价的.但是,一旦源代码被转换为机器代码,实际的空指针值可能不是0(并且所有这些比较都将与实际的空指针值相对). (4认同)
  • @AlexanderSupertramp:我的意思是程序执行的*运行时环境*可以使用0以外的值来表示"空"指针(即,定义良好的*无效*地址值).在某些系统上,"0"可能是程序可以访问的完全有效的内存地址,而不同的值(例如,"0xFFFFFFFF")可以用于"null".但是,在*源代码*中,0值表达式总是用于表示"NULL"指针常量. (3认同)
  • 愚弄我,但现在恐怕我需要接受更多教育。当你有一个指针“ ptr”时,“ ptr == NULL”与“ ptr == 0”或“!ptr”不是完全一样吗?此实现依赖吗? (2认同)

joh*_*nes 11

C99标准的第6.3.2.3节说

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

§7.17也说

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

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

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


zwo*_*wol 7

在C中,有一个且只有一个上下文,其中有必要将空指针常量显式地转换为特定指针类型,以便程序正常运行.该上下文通过无类型函数参数列表传递空指针.在现代 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
Run Code Online (Sandbox Code Playgroud)

是的,那最后一个例子是未定义行为即使 NULL被定义为((void *)0),因为void *char *不是在通过非类型化的参数列表传递隐含互相转化的,即使他们是在其他地方.

"引擎盖下",这里的问题是不是只是用于空指针的位模式,但编译器可能需要知道确切的具体类型每个参数,以便正确地建立呼叫帧.(考虑MC68000,以其独立的地址和数据寄存器;指定指针参数一些ABI在地址寄存器,但数据寄存器整数参数传递也考虑任何ABI在哪里.int而且void *是不一样的大小,它是难以觉察罕见时下,但. C仍然明确规定void *并且char *不是相同的大小.)如果有一个函数原型,编译器可以使用它,但是非原型函数和可变参数不提供这样的帮助.

C++更复杂,我觉得没有资格解释如何.