假设NULL
在 C 中总是转换为 false 是否安全?
void *somePtr = NULL;
if (!somePtr) {
/* This will always be executed? */
}
还是应该对 的值进行明确的检查NULL
?
是的。NULL 的计算结果为假,因为 C 认为任何非零值为真,任何零值为假。NULL 本质上是zero
地址,并且在比较中被视为这样,我相信会被提升为 int 以进行布尔检查。我希望任何熟悉 C 的人都可以阅读您的代码,尽管我可能会明确地进行检查。
在 C 和 C++ 编程中,保证两个空指针比较相等;ANSI C 保证任何空指针在与整数类型比较时都等于 0;此外,宏 NULL 被定义为空指针常量,即值为 0(作为整数类型或转换为指向 void 的指针),因此空指针将与 NULL 比较。
'C' 语言可以追溯到 (void*)0 实际上可以是有效指针的时代。不久前,8080 和 Z80 微处理器在地址 0 处有一个中断向量。面对这样的架构选择,它只能让头文件声明 NULL 的值。那里有一些编译器,现在早已被遗忘,其中 NULL 不等于 (void*)0 (0xffff 是下一个替代方案),从而使您的 if() 语句具有未定义的行为。
C++ 仁慈地结束了这一点,空指针可以从 0 分配和测试。
假设任何事情都不安全。
显式检查也更清楚您正在测试什么。
我的 ISO/IEC 9899:TC3(委员会草案 — 2007 年 9 月 7 日)副本说:
6.3 转换
1 几个运算符自动将操作数值从一种类型转换为另一种类型。
6.3.2.3 指针
3 值为 0 [...] 的整数常量表达式称为空指针常量。如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证与指向任何对象或函数的指针不相等。
到目前为止,ptr!=0
对于每个 non-null 都是 true (1) ptr
,但它仍然是开放的,两个空指针如何比较。
6.5.9 等式运算符
5 [...] 如果一个操作数是指针而另一个是空指针常量,则空指针常量将转换为指针的类型。
6 两个指针比较相等当且仅当两者都是空指针时,两者都是 [...]
因此,ptr==0
是 1(并且ptr!=0
是 0),当且仅当ptr
是一个空指针。
6.5.3.3 一元算术运算符
5 逻辑否定运算符的结果!如果其操作数的值比较不等于 0,则为 0,如果其操作数的值比较等于 0,则为 1。结果的类型为 int。表达式 !E 等价于 (0==E)。
所以同样适用于!ptr
。
6.8.4.1 if 语句
1 if 语句的控制表达式应具有标量类型。
2 在这两种形式中,如果表达式比较不等于 0,则执行第一个子语句。
注意,标量类型是算术类型或指针类型(参见“6.2.5 类型”,第 21 节)。放在一起,我们有:
if (ptr)
成功 ⇔ptr!=0
是 1 ⇔ptr
不是空指针。if (!ptr)
成功 ⇔ptr==0
是 1 ⇔ptr
是空指针。是的,if(!p)
有效并保证有效。
值为 0 的整型常量表达式或转换为 void * 类型的表达式称为空指针常量。如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证与指向任何对象或函数的指针不相等。
https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3
这意味着这(void*)0
是一个空指针。这也意味着 ifp
是一个指针,thenp==0
等价于p==(void*)0
.
这也意味着如果p
不是一个空指针,那么p==(void*)0
将评估为0
。
到现在为止还挺好。
将空指针转换为另一种指针类型会产生该类型的空指针。任何两个空指针应该比较相等。
http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p4
请注意,“任何两个空指针都应比较相等。” 这意味着如果p
是一个空指针,那么p==0
将评估为真,因为0
将提升到(void*)0
哪个是空指针。这也意味着没有非空指针可以等于空指针。
让我们看一下否定运算符。
逻辑否定运算符的结果!如果其操作数的值比较不等于 0,则为 0,如果其操作数的值比较等于 0,则为 1。结果的类型为 int。表达式 !E 等价于 (0==E)。
http://port70.net/~nsz/c/c11/n1570.html#6.5.3.3p5
这告诉我们,!p
与p==0
定义相同,p==(void*)0
与上面提到的相同。
考虑到所有空指针都相等这一事实,这意味着如果是空指针,p==(void*)0
则只能评估为真,如果p
不是空指针,则只能评估为假p
。
所以是的,这是检查是否为空指针if(!p)
的一种非常安全的方法。p
NULL 只是一个预处理器定义。它在 stdio.h 中。通常,只有疯子才会重新定义它,但这是可能的。一个例子:
#include <stdio.h>
#ifdef NULL
#undef NULL
#endif
#define NULL 1
void main()
{
if (NULL)
printf("NULL is true\n");
else
printf("NULL is false\n");
}
此代码将打印“NULL 为真”。如果您不相信我,请尝试一下。您的编译器甚至可能不会警告您您正在做一些奇怪的事情。
宏 NULL 在 <stddef.h> (和其他头文件)中定义为空指针常量;
值为0的整数常量表达式,或转换为 void * 类型的此类表达式称为空指针常量。
示例定义:
#define NULL ((void*)0)
if ( 表达式 ) 语句
if ( 表达式 ) 语句 else 语句
在这两种形式中,如果表达式比较不等于 0,则执行第一个子语句。在 else 形式中,如果表达式比较相等,则执行第二个子语句 §6.8.4.1 语言 133 ISO/IEC 9899:TC3 委员会草案 — 9 月 7 日, 2007 WG14/N1256 为 0。如果通过标签到达第一个子语句,则不执行第二个子语句。
所以是的,如果编译器符合 ISO C99,您可以假设下面的语句将始终执行。
if (!NULL) { statement; }
以上引用来自 ISO/IEC 9899:1999 (C99)。你可以在这里阅读。
是的,它:
C 标准 6.3.2.3
值为 0 的整型常量表达式或转换为 void * 类型的表达式称为空指针常量。如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证比较不等于指向任何对象或函数的指针。
和 6.3.2.6
任何指针类型都可以转换为整数类型。除非前面指定,结果是实现定义的。如果结果不能以整数类型表示,则行为未定义。结果不必在任何整数类型的值范围内。
-
当任何标量值转换为 _Bool 时,如果该值比较等于 0,则结果为 0;否则,结果为 1
NULL
被定义为一个常量指针,它保证指向内存中一个无用/不存在的地方。的大多数实现NULL
是,((void *)0)
但这不是强制性的。
在我看来,假设这一点并不总是安全的。因为,根据程序中包含的头文件,它可以被重新定义。但是按照标准,空指针常量保证不指向任何真实对象,并且有一个类型void *
。
在我们的例子中,声明void *somePtr = NULL
也可以void *somePtr = 0
是0
空指针。
“值为 0 的整型常量表达式,或这种类型转换为 void * 的表达式,称为空指针常量。如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证与指向任何对象或函数的指针不相等。” https://www.geeksforgeeks.org/few-bytes-on-null-pointer-in-c/
这只是意味着,*somePtr
在该特定点进行评估可能会导致错误。
另一个参考可以是https://www.gnu.org/software/libc/manual/pdf/libc.pdf在第 944 A.3 页关于 NULL 指针常量
是的,主要是。
首先,NULL 是一个 typedef。我可以通过在以前包含的标题中说出来来把你搞砸
#define NULL 1
这可能没有多大意义,但是从什么时候开始,其他人的代码才有意义呢?:)
此外,虽然它在语法上可能是安全的,但在语义上并不正确。NULL 表示“无”,既不是真或假,也不是布尔值、int 或字符串。它的意思是“一无所有的象征”。所以测试 NULL 更像是一个哲学问题:如果一棵树倒在森林里,并且if(listener)
,它会发出声音吗?
帮大家一个忙,并明确针对 NULL 进行测试。
*NULL 始终以 0x00L 为目标。您可以认为这是错误的,但要确保始终进行明确的检查。