我昨天正在面试一个中级软件工程职位的人,他提到在 C 中,NULL 并不总是零,并且他见过 NULL 不为零的 C 实现。我觉得这很可疑,但我想确定一下。有人知道他说得对吗?
(回复不会影响我对该候选人的判断,我已经向我的经理提交了我的决定。)
我假设你指的是空指针。 它保证比较等于
0
。1 但它不必用全零位表示。2
另请参阅有关空指针的 comp.lang.c FAQ。
§ 6.3.2.3说
值为 0 的整数常量表达式,或此类表达式强制转换为类型 void *,称为空指针常量)如果将空指针常量转换为 指针类型,生成的指针称为空指针,保证比较不相等 指向任何对象或函数的指针。
§ 7.17 还说
[...] NULL 扩展为实现定义的空指针常量 [...]
NULL 指针的地址可能与 0 不同,但它的行为与大多数情况下一样。
(这应该与旧的 C 标准相同,我现在手头没有)
空指针 constant 始终为 0。
NULL
宏可以由实现定义为裸 0
,或像 (void *) 0
这样的转换表达式,或其他一些零值整数表达式(因此“标准中的“实现定义”语言)。
空指针值可能不是0。当遇到空指针常量时,它将被转换为正确的空指针值。
在 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 指定在地址寄存器中传递指针参数,但在数据寄存器中传递整数参数。还要考虑 int
和
void *
大小不同的任何 ABI。如今这种情况已经很少见了,但 C 仍然明确规定
void *
char *
大小不同。[编辑:我不确定,但这可能不再被允许。])如果有一个函数原型,编译器可以使用它,但非原型函数和可变参数不提供此类帮助。
printf(“空%d “, 无效的); 结果==0
#define NULL (void *)1
printf(“空%d “, 无效的); 结果==1
从上面来看,当我们说NULL时。我们告诉编译器,NULL的内存将被视为空指针的意思,这没什么。就 if else 或 while 循环或任何其他条件中的条件而言,NULL 的零值有其自身的优势。
否则我们可以在程序中分配我们自己的 NULL 指针。