while(foo)与while(foo != NULL)的区别

5
能有人解释一下while(foo)while(foo != NULL)的等价性吗?另外:while(!foo)while(foo == NULL)呢?
我知道!代表"不",但这就是我所知道的全部了。

1
如果 foo 是一个指针,那么 while(foo) 的定义是 while(foo != NULL)。因此它们是等价的。 - M.M
1
可能是Can I use if (pointer) instead of if (pointer != NULL)?的重复问题。 - Daniel McIntosh
5个回答

7
假设foo是指针类型,while (foo)while (foo != NULL)完全等价。两者也等价于while (foo != 0)。(在我看来,while (foo != NULL)更清晰地表达了意图。)
在任何需要条件的上下文中(if()while()和其他一些情况),如果条件与零不相等,则将表达式视为true,如果条件与零相等,则将其视为falseNULL是一个宏,它扩展为实现定义的空指针常量。将指针值与NULL进行比较,对于空指针产生真值,对于任何非空指针产生假值。
常量0是一个空指针常量[*]。(这并不意味着空指针具有0x00000000或类似内容的相同位表示,尽管它经常具有;它意味着源代码中的常量0可以表示一个空指针。)正如您所期望的那样,将指针值与空指针常量进行比较可以告诉您该指针是否为空指针。在while (foo)中,与零的比较是隐含的——但它仍然测试foo是否为空指针。
更一般地说,while (foo)foo0进行比较,这等价于将其与适当类型的“零”进行比较。 while (foo)始终等价于while (foo != 0)。对于浮点值,它也等价于while (foo != 0.0)。对于字符值,它等价于while (foo != '\0')。并且,正如我们所看到的,对于指针值,它等价于while (foo != NULL)
(在C语言中,如果条件为假,则比较运算符始终产生一个值为0的值,如果条件为真,则产生一个值为1的值——但任何非零值都会通过与零的隐式不等式比较而被视为真。)
[*]空指针常量被定义为具有值0的整数常量表达式,或将这样的表达式强制转换为void*。空指针常量不一定是指针类型,但是将其转换为指针类型会产生空指针值。将指针值与0进行比较会导致0被隐式转换为指针类型,以便可以进行比较。

1
@chux:已更新。(0 既可以是 int 类型的表达式,也可以是空指针常量;我可以理解这可能会引起混淆。) - Keith Thompson
是的,对我来说也是这样,我正在享用一顿crow午餐。事实证明#define NULL 0是有效的,#define NULL ((void*)0)也是如此。后者有优势,但两者都是符合规范的。 - chux - Reinstate Monica
@chux: 有一个不常见的论点认为#define NULL ((void*)0)是无效的。(N1570 6.5.1p5没有说括号中的空指针常量是空指针常量。)但在实际使用中,它肯定是有效的。 - Keith Thompson

2
while(foo);

并且

while(foo != NULL)

这两个表达式等效,因为NULL是一个宏,可以扩展为0(void*)00L。在C语言中,0被认为是假(任何非零数字都是真),因此它们等同。

while(foo); //while foo isn't 0
while(foo != NULL) //expands to while(foo != 0) < while foo isn't 0

根据foo的类型而定。考虑使用double foowhile(foo);是可以的,但是while(foo != NULL)无法编译通过:“二元运算符!=的操作数无效”,这取决于NULL的定义方式,它应该是一个空指针常量,而不是像这里建议的0 - chux - Reinstate Monica
大多数实现确实使用0作为NULL。但我不同意,我的gcc 4.9.2使用#define NULL ((void *)0)。MS Visual 2010在编译为C时使用#define NULL ((void *)0)。也许你想到的是另一种语言如C++中如何定义NULL - chux - Reinstate Monica
@chux 我在我的答案中说过:“NULL是一个宏,可以扩展为0、(void*)0或者0L”。 - Alex Díaz
我断言NULL可以是(void*)0,但不是0,也不是0L。你的答案表示它可以是0,或(void*)00L。你的评论“大多数实现确实使用0作为NULL”,并没有得到任何例证,而对比起来有两个反例。 - chux - Reinstate Monica
根据与a3f的对话,得出结论:NULL 可以是 0(void *)0,我认错了。 - chux - Reinstate Monica
显示剩余2条评论

1

while (expression) 循环语句会在expression的值为非零时重复执行包含的代码块。当expression的值为0时,while循环结束。

如果expression是指针类型,则只要指针值不是std::nullptrNULL,就被认为是非0。

!运算符是布尔型的非运算符。

因此

 while (foo)

 while (foo != NULL)

它们在逻辑上彼此等价。因此,它们的逻辑反命题也相互等价。

3
std::nullptr 是 C++ 中的一个概念。我们现在正在进行一道 C++ 相关的问题。 - Bill Lynch

1
NULL是一个符号常量,你可以把它看作一种替换文本。编译器会将NULL替换成零,因此写while (foo != NULL)与写while (foo != 0)是相同的。请记住,==表示左侧的内容等于右侧的内容,而!=表示左侧的内容不等于右侧的内容。

在C语言中,数字0始终表示false,而其他数字表示true。因此,while (foo != 0)等同于说“只要foo为true,就运行此循环中的代码”。例如,while (1)等同于说“永远运行此循环中的代码”,因为1始终不等于零,因此始终为true。

在C语言中,如果你在需要真假值的地方只使用了foo,那么它等同于foo != 0,这就像是一种快捷方式,相当于在问“foo是否为真?”。!表示“非”,因此询问!foo等同于询问“foo不为真吗?”,换句话说,“foo是否为假?”。这意味着while (!foo)与“当foo为假时运行此循环中的代码”相同,这与while (foo == 0)相同,因为零表示假,这也与while (foo == NULL)相同,因为NULL表示零。
可以这样理解:只有当括号中的表达式为真(即非零)时,循环中的代码才会运行。因此,在while (foo)中,计算机将询问“foo是否非零?”,如果是,则继续循环。现在,如果你有while (!foo),计算机将询问“foo是否非非零?”,如果是,则继续循环。只有当括号中的表达式结果为假时,循环才会停止。

NULL 在多个标准头文件中定义,它不像 ifwhile 那样是一个关键字,你需要包含定义它的头文件才能使用它。其原理是当指针指向地址零时,它被称为“空指针”,这就是为什么它被称为NULL,它用于表达“零地址”而不是仅表示“零值”。

编辑: 正如 Matt McNabb 所指出的那样,NULL 可以被定义为 0 或者 (void *)0。为了消除任何疑虑,我引用了 C11 标准:

值为 0 的整数常量表达式,或者被转换为类型void *的这样一个表达式,称为空指针常量

稍后在文中提到:

NULL<stddef.h>(和其他头文件)中被定义为一个空指针常量;参见 7.19 节。

然后,在第 7.19 节中:

3 这些宏是

NULL

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

offsetof(type, member-designator)

它扩展为...

文本继续。因此,(void *)00 都是有效的实现定义的空指针常量。


NULL 可以是 0(void *)0 - M.M
@a3f 我引用“展开为实现定义的空指针常量NULL”的参考文献是C11 §7.19 3。那个“...等于零的整数常量…”在哪里? - chux - Reinstate Monica
@chux C11 n1570: §6.3.2.3p3。当然,实现可以自由选择一个与所有位为零不同的值来表示“NULL”,但是在“指针上下文”中,必须将整数0视为“NULL”。 - a3f
1
让我们在聊天中继续这个讨论。点击此处进入聊天室 - chux - Reinstate Monica
1
@danieltm64 得出结论,Matt McNabb 是正确的,NULL 可以是 0(void *)0 - chux - Reinstate Monica
显示剩余4条评论

0

while循环可以这样运行:

while(1) //This will run forever.

while(true) //This will run forever.

NULL就像0一样,所以如果你有while(foo != NULL),这意味着当foo不等于0时(也就是等于1或其他值),就运行它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接