我昨天面试了一位中级软件工程师,他提到在C语言中,NULL并不总是零,并且他曾经看到过C语言的实现中NULL不是零。我觉得这很可疑,但我希望确认一下。有人知道他是否正确吗?
(回复不会影响我的候选人评估,我已经提交了决策给我的经理。)
我假设你是指空指针。它保证与0
相等比较。1 但不一定用所有零位表示。2
另请参见有关空指针的comp.lang.c FAQ。
NULL
始终等于0
(空指针常量),第7.20.3.2节还指出,所有位都设置为零(例如在calloc
中的情况)不一定与表示为0.0f
或整数0
的表示相同。 - wklC99标准的§ 6.3.2.3规定
值为0的整数常量表达式,或将此类表达式强制转换为void *类型,被称为空指针常量。如果将空指针常量转换为指针类型,则生成的指针称为空指针,并保证与任何对象或函数的指针比较时均不相等。
§ 7.17还规定
[...] NULL展开成一个实现定义的空指针常量 [...]
空指针的地址可能与0不同,在大多数情况下它会像0一样工作。
(这应该与早期的C标准相同,我现在手头没有)
空指针常量始终为0。实现可以将NULL
宏定义为裸的0
,或者像(void *) 0
这样的转换表达式,或者一些其他零值整数表达式(因此标准中使用了"实现定义"语言)。
空指针值可能不是0。当遇到空指针常量时,它将被转换为适当的空指针值。
ptr
,ptr==NULL
难道不就等同于ptr==0
或!ptr
吗? 这个实现是否依赖于特定系统? - Mr Listerptr == 0
、ptr == NULL
和!ptr
都是等价的。然而,一旦源代码被转化为机器码,实际的空指针值可能并不是0(所有这些比较将与实际的空指针值进行比较)。 - John Bode*ptr == something
即使void * ptr = 0
? - Incerteza0
可能是您的程序可以访问的完全有效的内存地址,并且可能会使用不同的值(例如,0xFFFFFFFF
)来表示“null”。但是,在您的源代码中,始终使用值为0的表达式来表示NULL
指针常量。 - John Bodenull
指针的值等于存储在内存位置中的值,这个内存位置是任意选择(猜测)或由编译器在运行时选择的,当我们写char *p = NULL;
时,p指向这个内存位置。”时是否正确。请指正我。 - ajaysinghnegiexecl
,其中最后一个参数必须是显式转换为(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 ++更加复杂,我不觉得自己有资格解释如何处理。
int x = 0; void *p = (void *) x;
这里,x
的值为零,但x
并不是字面值零,因此p
不能保证是NULL
,在一些奇怪的平台上实际上也不会是NULL
。另一方面,void *q = 0;
总是将NULL
赋给q
,无论在什么平台上。 在这个上下文中,“字面值”有一个技术含义。搜索“整数字面量”。 - Dietrich Epp