NULL是否总是假?


69

是否可以安全地假设NULL在C语言中始终将其转换为false?

还是应该明确检查其价值NULL


我只是请您参考C-FAQ的问题5.3。它回答了这个确切的问题。

Answers:


72

是。NULL的计算结果为false,因为C认为任何非零值均为true,而任何零值为false。NULL本质上是zero地址,在比较中会这样处理,我相信对于布尔值检查,它会提升为int值。我希望熟悉C的任何人都可以阅读您的代码,尽管我可能会明确进行检查。

在C和C ++编程中,保证两个空指针比较相等。ANSI C保证在与整数类型进行比较时,任何空指针都将等于0。此外,宏NULL被定义为空指针常量,即值0(作为整数类型或转换为指向void的指针),因此空指针将比较等于NULL。

参考:http : //en.wikipedia.org/wiki/Null_pointer#Null_pointer


4
在某些情况下,显式检查可能会更清楚,但这是该语言所保证的C习惯用法。:)
Greg D

2
如果所有使用这些东西的c代码现在都无效,我会哭过:)
Johannes Schaub-litb

该标准在这个问题上不是很明确:可以保证将0强制转换为指针类型将是一个空指针,但是afaik它没有指定将null指针强制转换为int会发生什么!
Christoph

3
您虽然不会转换为int。您将0与指针进行比较。0是空指针常量。如果将空指针常量与指针进行比较,则空指针常量将成为正确类型的空指针。空指针将不等于每个有效指针。那就是他想要的
Johannes Schaub-litb

1
是的,jpalecek就是重点。“空指针”并不意味着“零位”。空指针是一个黑匣子。在if中,“!= 0”是隐式的,而在!ptr中,“ == 0”是隐式的。因此,如果执行if(ptr),它将把指针与空指针常量进行比较。
Johannes Schaub-litb

18

承担任何责任永远都不安全。

显式检查还可以使您更清楚地了解要测试的内容。


我们过去更进一步,为每个类型定义的项目定义了特定于类型的nil值:结构,整型,长整型等等。这样,条件语句将显示为“ if(foo!= fooNil){etc.}”。免责声明:我曾经在施乐公司为西蒙尼工作,所以我的大脑受到了污染。
彼得·罗威尔

1
@Peter罗威尔:在相当长的时间,我使用的#define NIL(X)(X)0()来实现大致这种效果:NIL(富),NIL(字符*),NIL(巴),等等
乔纳森Leffler

1
@乔纳森:用哪种语言-C或C ++?在C99中,这是不必要的,因为“任何两个空指针都应相等”。
Christoph

18

“ C”语言可以追溯到(void *)0实际上是有效指针的时代。不久之前,8080和Z80微处理器在地址0处有一个中断向量。面对这样的体系结构选择,除了让头文件声明NULL值外,它什么也不能做。有一些编译器在那里,现在早已被人们遗忘,其中NULL不等于(void *)0(0xffff是下一个替代方案),因此为if()语句提供了不确定的行为。

C ++尽心尽力地结束了这一点,空指针可以从0赋值,并且可以针对0进行测试。


在更改中,MMU可能比C ++更重要。
乔纳森·莱夫勒

1
关于您的C ++句子:错了。0不是空指针。0是空指针常量。无效* p = 0; 现在,p是空指针。这是有区别的,因为空指针具有指针类型,其中空指针常量具有整数类型并且是常量表达式
Johannes Schaub-litb

2
C99保证将0强制转换为指针类型也将是空指针-我从不费心阅读以前的标准,所以不知道何时发生……
Christoph

“给您的if()语句未定义的行为。” C常见问题解答不同意
jfs

1
我为mmu代码编写的每个拱形上的0仍然是一个完美有效的地址。这只是许多C运行时的惯例,使它的使用无效。在现代Linux中,您可以使用MAP_FIXED将root映射为0。在不很久以前的UNIX中,通常将只读的零页映射到零,以便strcmp(NULL,...)不会引起错误,而只会匹配一个空字符串。
mevets

14

是的(至少对于任何符合标准的C编译器!)

comp.lang.c常见问题解答

问:用于测试非空指针的缩写指针比较``if(p)''是否有效?如果空指针的内部表示非零怎么办?

答:总是有效的。


7

我的ISO / IEC 9899:TC3(委员会草案,2007年9月7日)副本:

6.3转换

1一些运算符会自动将操作数值从一种类型转换为另一种类型。

6.3.2.3指针

3值为0 [...]的整数常量表达式称为空指针常量。如果将空指针常量转换为指针类型,则保证生成的指针(称为空指针)将不相等的值与指向任何对象或函数的指针进行比较。

到目前为止,ptr!=0对于每个非null来说都是true(1)ptr,但是它仍然是开放的,如何比较两个null指针。

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为空指针。


4

是的,if(!p)有效并且保证可以正常工作。

值为0的整数常量表达式,或强制转换为void *类型的表达式,称为空指针常量。如果将空指针常量转换为指针类型,则保证生成的指针(称为空指针)将不相等的值与指向任何对象或函数的指针进行比较。

https://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3

这意味着它(void*)0是一个空指针。这也意味着,如果p是指针,则p==0等于p==(void*)0

这也意味着,如果p不是非null指针,p==(void*)0则将求值为0

到现在为止还挺好。

将空指针转换为另一种指针类型将产生该类型的空指针。任何两个空指针应比较相等。

http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p4

注意:“任何两个空指针都应相等。” 这意味着,如果p为空指针,p==0则将评估为true,因为0将被提升为(void*)0空指针。这也意味着非空指针不能等于空指针。

让我们看一下求反运算符。

逻辑否定运算符的结果!如果其操作数的值比较不等于0,则为0;如果其操作数的值比较等于0,则为1。结果的类型为int。表达式!E等效于(0 == E)。

http://port70.net/~nsz/c/c11/n1570.html#6.5.3.3p5

这告诉我们这!pp==0定义相同,也p==(void*)0与上述相同。

考虑到所有空指针都相等的事实,这意味着,p==(void*)0如果p为空指针,则只能求值为true;如果不是空指针,则只能求值为false p

所以,if(!p)是一种检查是否p为空指针的绝对安全的方法。


3

NULL只是一个预处理器定义。在stdio.h中。通常,只有一个疯狂的人才能重新定义它,但是有可能。一个例子:

此代码将显示“ NULL为true”。如果您不相信我,请尝试一下。您的编译器甚至可能不会警告您您做的事情很奇怪。


1
但是,如果将NULL定义为1并分配somePtr = NULL,则至少会收到警告,提示您正在将整数转换为指针。
Mark James

有趣。您可以通过以下操作使其不发出警告:#define NULL(void *)1
bdowling

3
那将是完全错误的。出于某种原因,编译器和C标准库捆绑在一起。如果您搞砸了,那就是打破了。
Spidey 2012年

1
是的,如果您重新定义它,含义将会改变,但这几乎没有关系。
klutt

2

是的,它:

C标准6.3.2.3

值为0的整数常量表达式或强制转换为void *的表达式称为空指针常量。如果将空指针常量转换为指针类型,则保证生成的指针称为空指针。比较不等于指向任何对象或函数的指针。

和6.3.2.6

任何指针类型都可以转换为整数类型。除非先前指定,否则结果是实现定义的。如果结果不能用整数类型表示,则行为未定义。结果不必在任何整数类型的值范围内。

--

当任何标量值转换为_Bool时,如果该值比较等于0,则结果为0;否则,结果为0。否则,结果为1


2


什么是NULL?

宏NULL在<stddef.h>(和其他标头)中定义为空指针常量


空指针常量的值是多少?

值为0的整数常量表达式,或强制转换为void *类型的表达式,称为空指针常量。

示例定义:


评估if(NULL)

if(表达式)语句

if(表达式)语句else语句

在两种形式中,如果表达式比较不等于0,则执行第一个子语句。在else形式中,如果表达式比较等于相等,则执行第二个子语句。§6.8.4.1语言133 ISO / IEC 9899:TC3委员会草案– 9月7日, 2007 WG14 / N1256为0。如果通过标签到达第一个子语句,则不执行第二个子语句。


因此,是的,如果编译器符合ISO C99,则可以假定以下语句将始终执行。


以上引用来自ISO / IEC 9899:1999(C99)。您可以在这里阅读。


0

NULL被定义为保证指向内存中无用/不存在位置的常量指针。的大多数实现NULL是,((void *)0)但并非必须如此。


在C语言中,这是正确的。请注意,在C ++中,它通常只是0,主要是因为没有显式强制转换就无法将void指针转换为任何其他类型。
乔纳森·莱夫勒

3
在任何正确的C编译器中,即使空指针并不真正包含0,“ NULL == 0”也为true。您不能做的是memzero一个指针,然后假定它为NULL。
Segfault上尉,09年

0

据我说,假设并非总是安全的。由于可以根据程序中包含的标题来重新定义它。但是根据标准,可以保证空指针常量不指向任何实际对象并且具有类型void *

在我们的例子中,声明void *somePtr = NULL也可以void *somePtr = 0使用0null指针。

“一个整数常量表达式,其值为0,或将这种表达式强制转换为void *类型,称为空指针常量。如果将空指针常量转换为指针类型,则可以保证生成的指针(称为空指针)与指向任何对象或函数的指针不相等。https://www.geeksforgeeks.org/few-bytes-on-null-pointer-in-c/

这仅表示*somePtr在该特定点进行评估可能会导致错误。

关于空指针常量的另一个参考可以是第944 A.3页的https://www.gnu.org/software/libc/manual/pdf/libc.pdf


-2

是的,主要是。

首先,NULL是typedef。我可以在先前包含的标题中说出您的名字来

这可能没有多大意义,但是既然别人的代码什么时候才有意义?:)

另外,尽管它在语法上可能是安全的,但在语义上却不正确。NULL表示“无”,即true或false或布尔值或int或string。它的意思是“什么都没有的象征”。因此,测试NULL更像是一个哲学问题:如果一棵树落在森林中,并且if(listener)发出声音?

请每个人帮个忙,并明确测试NULL。


4
@Matt:您不是指宏:-)吗?

-6

* NULL始终以0x00L为目标。您可以认为这是错误的,但要确保始终进行显式检查。


2
NULL是特定于实现的。在某些情况下,例如嵌入式计算机,您可能希望访问RAM中的第一个字节,该字节的首地址为0。标准将NULL定义为指针,该指针保证不指向RAM中的任何可用点。
dreamlax

取消引用NULL本身或引用null指针会调用未定义的行为-或在具有MMU的足够大的计算机上取消核心转储或等效操作。“全世界都是VAX”综合症不久前就死了-在那儿,取消引用NULL确实产生了零字节。
乔纳森·勒夫勒

取消引用时,vax计算机的低128字节会导致访问冲突。您是否有一个程序可以证明所观察到的症状?
EvilTeach
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.