正如comp.lang.c FAQ所说,存在空指针不是所有位都为零的体系结构。因此,问题是什么实际上检查了以下结构:
void* p = get_some_pointer();
if (!p)
return;
我是在将p
与机器相关的空指针进行比较,还是和算术零进行比较?
应该写:
void* p = get_some_pointer();
if (NULL == p)
return;
是我太神经质了,还是需要准备这些体系结构呢?
正如comp.lang.c FAQ所说,存在空指针不是所有位都为零的体系结构。因此,问题是什么实际上检查了以下结构:
void* p = get_some_pointer();
if (!p)
return;
我是在将p
与机器相关的空指针进行比较,还是和算术零进行比较?
应该写:
void* p = get_some_pointer();
if (NULL == p)
return;
是我太神经质了,还是需要准备这些体系结构呢?
对于C++来说,这与我们所拥有的内容非常接近,再加上
nullptr
语法糖。运算符==
的行为由以下规定:此外,成员指针可以进行比较,或者进行成员指针和空指针常量的比较。通过进行成员指针转换(4.11)和限定符转换(4.4),将它们转换为共同的类型。如果一个操作数是空指针常量,则共同类型是另一个操作数的类型。否则,共同类型是成员指针类型,类似于其中一种操作数的类型(4.4),其cv限定符签名(4.4)是操作数类型的cv限定符签名的并集。[注意:这意味着任何成员指针都可以与空指针常量进行比较。-结束语]
这导致将0
转换为指针类型(与C相同)。对于否定运算符:
逻辑否定运算符!的操作数在上下文中被转换为布尔类型(第4节);如果转换后的操作数为true,则其值为true,否则为false。结果的类型是布尔类型。
这意味着!p
的结果取决于如何从指针到bool
进行转换。标准规定:
零值、空指针值或空成员指针值被转换为false;
因此,在C++中,if (p==NULL)
和if (!p)
执行的操作相同。
!E
与E == 0
相同(C11草案6.5.3.3/7)等。 - alkif (! p)
是被定义良好的。但是 if (p)
呢?这可能看起来很荒谬,但是这个答案中的引用实际上并没有保证它的语义。我相信 §6.8.4.1p2 才是关键。 - Konrad Rudolph在实际机器中,空指针是否是全零比特并不重要。假设p
是一个指针:
if (!p)
使用 == nullptr
检测指针变量 p
是否为空指针是合法的方法,而且它与以下表达式等价:
if (p == NULL)
你可能对另一篇C-FAQ文章感兴趣:这很奇怪,NULL保证为0,但空指针却不是?
以上适用于C和C++。请注意,在C++(11)中,建议使用nullptr
表示空指针字面常量。
此答案适用于C语言。
不要混淆NULL
和空指针。 NULL
只是一个宏,保证是一个空指针常量。空指针常量保证为0
或(void*)0
。
C11 6.3.2.3中的说明:
值为0的整型常量表达式,或者这种类型强制转换成void* 的表达式称为空指针常量66)。如果将空指针常量转换为指针类型,则得到的指针称为null指针,并保证与任何对象或函数的指针比较不相等。
66) 宏NULL在stddef.h和其他头文件中定义为null指针常量;参见7.19。
7.19 中的说明:
宏有
NULL
它扩展为实现定义的null指针常量;
在NULL
的情况下,实现定义为0
或(void*)0
。 NULL
不能是其他任何东西。
然而,当把空指针常量赋给指针时,你会得到一个空指针,它的值可能不是零,尽管它与空指针常量比较相等。代码if(!p)
与NULL
宏没有关系,它将空指针与算术值0进行比较。
因此,理论上,像int* p = NULL
这样的代码可能会产生一个空指针p
,其值与零不同。
0
的内部编译错误(ICE),例如NULL
可以被定义为(1-1)
。 - M.M0
相等。 - M.M!
的定义是,!x
与x == 0
相同。由于在关于NULL
指针的预期效果上,x == 0
具有期望的效果,因此在NULL
指针不等于0的平台上,!x
确实会检查NULL
。 - fuz早期的STRATUS计算机在所有语言中将空指针作为1处理,这对于C语言造成了问题。
因此,他们的C编译器允许将指针的0和1进行比较,以返回true。
这样做可以实现:
void * ptr=some_func();
if (!ptr)
{
return;
}
即使在调试器中看到ptr
的值为1,也可能返回空指针null ptr
if ((void *)0 == (void *)1)
{
printf("Welcome to STRATUS\n");
}
实际上会打印出“欢迎来到STRATUS”
如果你的编译器足够好,那么只有两个问题(且只有两个问题)需要注意:
1: 静态默认初始化(未被分配)的指针不会具有NULL值。
2: 在结构体或数组上使用memset()函数,或者使用calloc()函数将不会把指针设为NULL。
... = 0;
一样。关于(1),你对于符合标准的编译器是错误的。 - Nemo
C
还是C++
的问题吗?在C++
中,你应该总是使用nullptr
。 - BeyelerStudiosNULL
是算术零(即使底层地址不是物理内存地址零)。这意味着在C++中,您可以将NULL
和0
视为相同的,并且它们实际上是无法区分的。请注意,这也适用于C,只需做一个类型转换即可。 - Konrad Rudolphmemset
清零时可能会得到非空指针。值得一提的是,出现此问题的奇特硬件类型很可能会违反关于现代架构的其他常见假设。更重要的是,虽然我欣赏在实践中编写可移植代码的愿望,但在这些极端平台上,非常规的C/C++代码实际上永远不会起作用,除非它已经在奇异的硬件上进行了测试,至少这是我的经验。 - doynax