当空指针不全是零时,如何正确编写C / C ++代码

正如comp.lang.c常见问题解答所说的那样,有一些体系结构的空指针并不全是零。 所以问题是实际检查下面的结构:

void* p = get_some_pointer(); if (!p) return; 

我比较p与机器相关的空指针或我比较p与算术零?

我应该写吗?

 void* p = get_some_pointer(); if (NULL == p) return; 

而是为这样的架构做好准备,还是只是我的偏执狂?

根据C规范:

值为0的整数常量expression式,或者转换为void *types的expression式称为空指针常量。 55)如果一个空指针常量被转换为一个指针types,那么被调用的空指针所产生的指针将被保证不等于一个指向任何对象或函数的指针。

所以0是一个空指针常量。 如果我们将它转​​换为一个指针types,那么对于某些体系结构,我们将得到一个空指针,该指针可能不是全零。 接下来让我们来看看spec中关于比较指针和空指针常量的内容:

如果一个操作数是一个指针,另一个是空指针常量,则空指针常量被转换为指针的types。

让我们考虑一下(p == 0) :首先将0转换为空指针,然后将p与一个空指针常量进行比较,其实际位值取决于架构。

接下来,看看规范说的否定运算符:

逻辑否定运算符的结果! 如果操作数的值不等于0,则为0,如果操作数的值等于0,则为1.结果的types为int。 expression式!E相当于(0 == E)。

这意味着(!p)相当于(p == 0) ,它是根据规范对机器定义的空指针常量进行testing的。

因此,即使在空指针常量不是全零位的架构上,也可以安全地写if (!p)

至于C ++,空指针常量被定义为:

一个空指针常量是一个整型常量expression式(5.19),它的整数types的值是零,或者是std :: nullptr_ttypes的一个prvalue。 空指针常量可以转换为指针types; 结果是该types的空指针值,并且可以与对象指针或函数指针types的每个其他值区分开来。

这与我们对C的接近,加上nullptr语法糖。 运算符==的行为由以下定义:

另外,可以比较指向成员的指针,或指向成员的指针和空指针常量。 指向成员转换(4.11)和资格转换(4.4)的指针被执行以使它们成为通用types。 如果一个操作数是空指针常量,则通用types是另一个操作数的types。 否则,通用types是指向与操作数types类似(4.4)的成员types的指针,具有操作数types的cv限定签名的联合的cv限定签名(4.4)。 [注意:这意味着任何指向成员的指针都可以与空指针常量进行比较。 – 结束注意]

这导致0到指针types的转换(对于C)。 对于否定操作符:

逻辑否定运算符的操作数! 被上下文转换为bool(条款4); 如果转换的操作数为true,则其值为true,否则为false。 结果的types是bool。

这意味着!p结果取决于如何执行从指针到bool转换。 标准说:

零值,空指针值或空成员指针值被转换为false;

所以if (p==NULL)if (!p)在C ++中也做同样的事情。

如果空指针在实际机器中是全位零或不是没关系。 假设p是一个指针:

 if (!p) 

总是testingp是否为空指针的合法方式,它总是等价于:

 if (p == NULL) 

您可能对另一篇C-FAQ文章感兴趣: 这很奇怪。 NULL保证为0,但空指针不是?


以上对于C和C ++都是如此。 请注意,在C ++(11)中,最好使用nullptr作为空指针文字。

这个答案适用于C.

不要将NULL与空指针混淆。 NULL只是一个保证是空指针常量的macros。 空指针常量保证是0(void*)0

从C11 6.3.2.3:

值为0的整数常量expression式或者转换为void *types的expression式称为空指针常量66)。 如果一个空指针常量被转换为一个指针types,那么称为空指针的结果指针保证将不等于指针的任何对象或函数进行比较。

66)macrosNULL在<stddef.h>(和其他头文件)中定义为空指针常量; 见7.19。

7.19:

macros是

空值

它扩展为一个实现定义的空指针常量;

NULL的情况下定义的实现是0(void*)0NULL不能是其他的东西。

然而,当一个空指针常量被转换为一个指针时,你会得到一个空指针 ,它可能不具有零值,即使它与一个空指针常量相等。 代码if (!p)NULLmacros无关,你正在比较一个空指针和算术值零。

所以理论上,像int* p = NULL这样的代码可能会导致一个不同于零的空指针p

当天,STRATUS计算机的空指针在所有语言中都是1。

这导致C的问题,所以他们的C编译器将允许指针比较0和1返回true

这将允许:

 void * ptr=some_func(); if (!ptr) { return; } 

即使在debugging器中可以看到ptr的值为1,也要return null ptr

 if ((void *)0 == (void *)1) { printf("Welcome to STRATUS\n"); } 

实际上会打印“欢迎来到STRATUS”

如果你的编译器不错,有两件事(只有两件事)要注意。

1:静态默认初始化(即未分配)指针不会有NULL。

2:结构体或数组上的memset()或扩展名calloc()不会将指针设置为NULL。