C语言中的"!!"是什么?

58

我遇到了以下代码片段:

pt->aa[!!(ts->flags & MASK)] = -val;
  1. !!(双感叹号/双非运算符)在C语言中代表什么意思?
  2. (!!NULL) == NULL是否成立?

3
相关链接:https://dev59.com/9Wkv5IYBdhLWcg3w3EWl - user000001
1
在我的代码"十进制转二进制:大小独立:第一种方法"中,我使用了!!来将int转化为01位。 - Grijesh Chauhan
3
请注意,这段代码存在问题。问题1:使用布尔值作为数组索引。问题2:使用奇怪的!!语法,只会让读者感到困惑(正如我们从这个问题中可以看出的那样)。问题3:通过一个不必要复杂的表达式计算数组索引,它应该被移到自己的一行代码上。所有这些都指向同一件事:我怀疑我们有一个程序员认为他们非常聪明,但实际上在正确的程序设计和代码维护方面还有很多需要学习的地方。 - Lundin
5
提到“可能是重复”的提名时,请记住C和C++是两种不同的语言。因此,我认为有两个独立的问题和独立的答案是合理的。即使今天语义相似,未来的标准可能会改变这种状态。 - Jan
@Jan,这个问题标记为 [tag:c],相关问题也标记为 [tag:c]。如果标签不正确,应进行更新。 - zzzzBov
显示剩余5条评论
7个回答

91

!是否定运算符。所以!!是双重否定(即肯定)。重要的是结果将是一个int

  • 如果x == 0,则!!x就是!!0,即!1,即0
  • 如果x != 0,则!!x就是!!(!0),即!!1,即!0,即1

!!通常用于将任何非零值转换为1,同时确保0仍然保持为0。

实际上,!!NULL == NULL,因为!!NULL == !!0,而!!0 == !1,最终!1 == 0

因此,在你引用的这段简短的代码中,如果括号中的表达式的值为NULL,则数组下标将为0,否则为1


2
结果的值是 int 类型的 01 - pmg
9
如果NULL0,那么(!!NULL) == NULL为真。如果NULL(void*)0这样的值,则可能会出现指针与整数比较的警告。使用!!的主要目的是将“真值”和“假值”表达式结果转换为整数。 - Mike DeSimone
1
无论NULL的类型如何,它都不应该是一个警告。!ptr是正常用法。 - R.. GitHub STOP HELPING ICE
3
不完全正确。!!false0!!true1 - Justin ᚅᚔᚈᚄᚒᚔ
2
关于NULL的那一段是不正确的...即使除了(void*)警告, 在某些罕见的情况下,计算机中空指针的值可能为非零,因此C标准允许NULL为任何值,例如它可以是(void*)INT_MAX,这会使您的语句变成错误的。我不确定百分之多少的C代码在这样的系统上运行失败,但没有必要鼓励人们做出非标准的假设并编写更多非标准代码。 - Theodore Murdock
显示剩余9条评论

29

这种技巧通常被(滥)用于通过反复应用布尔非运算符将任何值转换为int类型的0或1。

例如:!56的值为0,因为当将56视为布尔值时它是“true”。这意味着!!56的值为1,因为!0为1。


1
你说“被滥用”,但这是一个常见的习语。你有什么简洁的替代建议吗? - PC Luddite
@PCLuddite 好吧,我确实说了“(滥)用”,暗示滥用指控有点玩笑。我可能会建议使用pt->aa[(ts->flags & MASK) != 0],因为我认为这更清晰明确。 - unwind
它有些古怪,但并不是 C 最糟糕的滥用之一,也绝对不是 C++ 最糟糕的滥用之一。那种语言只是为了代码滥用而建立的。 - PC Luddite

7

!EE == 0相同,因此!!E(E == 0) == 0相同。 !!用于规范布尔值。


4
在C99中,您可以将其替换为


#include <stdbool.h>


pt->aa[(bool)(ts->flags & MASK)] = -val;

当然,如果您的代码需要可移植到C89,则最好使用!!技巧。
pt->aa[(ts->flags & MASK)!=0] = -val;

或者

pt->aa[(ts->flags & MASK)?1:0] = -val;

生成的代码肯定是完全相同的。

4

它将数字转换为规范的布尔值。

需要注意,在这种情况下这一步非常重要,因为结果将用于索引数组。


2
  1. !!x 就是 !(!x)
  2. 如果将 NULL 定义为 0,则 !!NULL == !!0 == !(!0) == !(1) == 0

1

在某些情况下,例如在条件语句中进行赋值操作且有多个表达式时,使用!!是让编译器保持安静的不错方式。

int _blah = 100;
int *blah;
if ( _blah > 100 && !!(blah = &_blah) ) {
// do stuff
}

我不推荐这样做 - 警告通常存在是为了强制执行良好的编码实践。

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