正如comp.lang.c常见问题解答所说,在某些体系结构中,空指针并非全为零。所以问题是什么实际上检查以下构造:
void* p = get_some_pointer();
if (!p)
return;
我是在p
与机器相关的空指针进行比较p
,还是在与算术零进行比较?
我应该写
void* p = get_some_pointer();
if (NULL == p)
return;
而是为这种架构做好准备,还是只是我的偏执?
正如comp.lang.c常见问题解答所说,在某些体系结构中,空指针并非全为零。所以问题是什么实际上检查以下构造:
void* p = get_some_pointer();
if (!p)
return;
我是在p
与机器相关的空指针进行比较p
,还是在与算术零进行比较?
我应该写
void* p = get_some_pointer();
if (NULL == p)
return;
而是为这种架构做好准备,还是只是我的偏执?
NULL
算术零,至少在C ++中(我也认为在C中以强制转换为模)。这是真实的,即使底层的地址不是物理内存地址为零。也就是说,在C ++中,你可以把NULL
和0
相同的,他们实际上没有什么区别。
memset
为零可能会导致非NULL指针。顺便说一句,可能遇到此问题的奇异硬件的类型可能会破坏关于现代体系结构的许多其他常见假设。更重要的是,尽管我很欣赏在实践中编写可移植代码的愿望,但除非实际已经在深奥的硬件上进行了测试,否则非平凡的C / C ++代码永远无法在这些类型的遥不可及的平台上运行,至少这是我的经验。
Answers:
根据C规范:
值为0的整数常量表达式,或强制转换为void *类型的表达式,称为空指针常量。55)如果将空指针常量转换为指针类型,则保证生成的指针(称为空指针)将不相等的指针与指向任何对象或函数的指针进行比较。
所以0
是一个空指针常数。如果将其转换为指针类型,则将获得一个空指针,对于某些体系结构,该指针可能不是全零位。接下来,让我们看看规范关于比较指针和空指针常量的内容:
如果一个操作数是一个指针,另一个是空指针常量,则将空指针常量转换为指针的类型。
让我们考虑一下(p == 0)
:首先0
将其转换为空指针,然后p
将其与一个空指针常量进行比较,该常量的实际位值取决于体系结构。
接下来,查看规范关于否定运算符的内容:
逻辑否定运算符的结果!如果其操作数的值比较不等于0,则为0;如果其操作数的值比较等于0,则为1。结果的类型为int。表达式!E等效于(0 == E)。
这意味着,按照规范,这(!p)
等效于针对机器定义的空指针常量进行测试。(p == 0)
p
因此,if (!p)
即使在空指针常量并非全零位的体系结构上,您也可以安全地编写。
对于C ++,空指针常量定义为:
空指针常量是整数类型的整数常量表达式(5.19)prvalue,其值为零,或类型为std :: nullptr_t的prvalue。空指针常量可以转换为指针类型。结果是该类型的空指针值,并且可以与对象指针或函数指针类型的所有其他值区分开。
这与C语言的nullptr
语法和语法糖差不多。操作员的行为==
定义为:
另外,可以比较指向成员的指针,或者指向成员的指针和空指针常量。执行指针到成员转换(4.11)和资格转换(4.4)的操作,以将它们转换为通用类型。如果一个操作数是空指针常量,则公共类型是另一操作数的类型。否则,通用类型是指向类似于操作数之一的类型的成员类型(4.4)的指针,并且具有cv限定签名(4.4),该签名是操作数类型的cv限定签名的并集。[注意:这意味着可以将指向成员的任何指针与一个空指针常量进行比较。—尾注]
这导致转换为0
指针类型(与C一样)。对于求反运算符:
逻辑否定运算符的操作数!在上下文中转换为bool(第4条);如果转换后的操作数为true,则其值为true;否则为false。结果的类型是布尔。
这意味着的结果!p
取决于如何bool
执行从指针到的转换。该标准说:
零值,空指针值或空成员指针值将转换为false;
因此if (p==NULL)
,if (!p)
在C ++中也做同样的事情。
!E
与E == 0
(C11草案6.5.3.3/7)一样。–
int
。根据C ++ 11 5.3.1 / 9,“结果的类型为bool
”。
if (! p)
是明确的。但是呢if (p)
?这看似荒谬,但此答案中的引号实际上并不能保证其语义。我相信§6.8.4.1p2可以。
在实际机器中,空指针是否为全零都没关系。假设p
是一个指针:
if (!p)
始终是测试是否p
为空指针的合法方法,并且始终等效于:
if (p == NULL)
您可能对另一篇C-FAQ文章感兴趣:这很奇怪。NULL是否保证为0,但null指针不是?
以上对于C和C ++均适用。请注意,在C ++(11)中,首选将其nullptr
用于空指针文字。
此答案适用于C。
不要NULL
与空指针混淆。NULL
只是一个保证为空指针常量的宏。空指针常量保证为0
或(void*)0
。
从C11 6.3.2.3:
值为0的整数常量表达式,或强制转换为void *类型的表达式,称为空指针常量66)。如果将空指针常量转换为指针类型,则保证生成的指针(称为空指针)将不相等的值与指向任何对象或函数的指针进行比较。
66)宏NULL在<stddef.h>(和其他头文件)中定义为空指针常量;见7.19。
7.19:
宏是
空值
扩展为实现定义的空指针常量;
在的情况下定义的实现NULL
是0
或(void*)0
。NULL
不能是别的。
但是,将空指针常量分配给指针时,您会得到一个空指针,即使它等于一个空指针常量,它也可能不具有零值。该代码if (!p)
与NULL
宏无关,您将空指针与算术值零进行比较。
因此,从理论上讲,类似这样的代码int* p = NULL
可能会导致一个空指针p
,该指针不同于零。
0
,例如,NULL
可以定义为(1 - 1)
(void *)0
有
0
!
运算符被定义为使得!x
相同x == 0
,由于x == 0
具有相对于预期的效果NULL
指针,!x
不检查用于NULL
一平台,其中上NULL
指针不数值等于0
过去,STRATUS计算机在所有语言中的空指针均为1。
这给C带来了问题,因此他们的C编译器将允许指针比较0和1返回true。
这将允许:
void * ptr=some_func();
if (!ptr)
{
return;
}
要return
在一个空PTR,即使你可以看到,ptr
在调试器中有值1
if ((void *)0 == (void *)1)
{
printf("Welcome to STRATUS\n");
}
实际上会打印“ Welcome to STRATUS”
calloc
如这样的程序)很有用。那个完全相同的参数如何不适用于指针类型?
如果您的编译器不错,则有两件事(只有两件事)需要注意。
1:静态默认初始化(即未分配)的指针中将没有NULL。
2:结构或数组上的memset()或扩展名calloc()不会将指针设置为NULL。
nullptr
。如果您的编译器不执行此操作,那就不好了……
... = 0;
。对于以前的每个符合标准的编译器,您都(1)错了。
C
还是C++
问题吗?在C++
你应该总是使用nullptr