数组索引中的感叹号有什么作用?

27

在浏览我们组织的代码库时,我发现了这个小宝石:

RawParameterStorage[!ParameterWorkingIdx][ParameterDataOffset] = ...

这段代码是否有效?(它可以编译)这里的感叹号是什么作用?

使用反转 ~ 运算符可能是有意义的,因为在布尔表达式中常常与非 ! 运算符混淆。然而,在数组索引上强制使用非 ! 运算符似乎没有逻辑意义。有什么想法?

4个回答

43

!ParameterWorkingIdx 意味着 ParameterWorkingIdx 值为 0,如果是,则!ParameterWorkingIdx 求值为 true,可能会隐式转换为索引类型(例如,在数组中为整数索引器的1),否则,求值为 false

  • 如果 ParameterWorkingIdx == 0,则 [!ParameterWorkingIdx] == [1]

  • 如果 ParameterWorkingIdx != 0,则 [!ParameterWorkingIdx] == [0]

还取决于其他因素,如:

  • ParameterWorkingIdx 的类型。

  • 按照 ParameterWorkingIdx 的类型重载 ! 运算符

  • 按照 RawParameterStorage 的类型重载索引器。

  • 等等...


12
你" + ["已经", "尚未"][!loggedIn()] + "登录。" ← 这是JavaScript中相同意思的示例。虽然有点不好看,但这并不是C语言的错! - j6m8
我认为你需要从第一句中删除“different than”。以它目前的形式,它的意思与你想要表达的相反。 - CodesInChaos
1
@j6m8 其实这不适用于JS,因为属性会转换为字符串,布尔值最终会变成 "true" / "false"。所以要么使用 "You "+["are not", "are"][+loggedIn()]+ " logged in",要么使用 "You "+{false:"are not", true:"are"}[loggedIn()]+ " logged in" - Bergi
3
C就像小猫一样,而C++更像是一个动物园。用自己压倒一切的方式来看,它也很不错;) - Quentin

19

猜测这是一个双缓冲模式。 ParameterWorkingIdx 会在0和1之间来回切换(可能使用代码 ParameterWorkingIdx = !ParameterWorkingIdx;)。

然后,RawParameterStorage[ParameterWorkingIdx] 将是当前的缓存,而RawParameterStorage[!ParameterWorkingIdx] 将是先前的缓存。


8

对数组索引使用非 ! 操作符似乎没有逻辑意义

这样做可能有意义: 它只会将零转换为一,将其他数字转换为零。

我们可以从这段代码中推断出,RawParameterStorage 可能在顶层有两个元素。

附注:在此,我假设 RawParameterStorage 是一个数组(正如你所说)。此外,我假设 ParameterWorkingIdx 是一个整数(因为它的名字暗示了这一点)。例如,如果其中一个是带有重载操作符的类,则语义可能完全不同。


2
UV'd 但这假设 ParameterWorkingIdx 不是一个具有重载 ! 操作符的类的实例。建议 OP 进行检查。 - Bathsheba
@Bathsheba:说得好。为了完整起见,我会再加上几句话。 - NPE

3

这是有效的代码吗?

是的。假设ParameterWorkingIdx是一个int,对于!ParameterWorkingIdx,当与operators !一起使用时,它将在上下文中转换为bool

值为零(对于整数、浮点数和未作用域枚举)、空指针和空指针成员值变为false。所有其他值都变为true

然后进行整数提升以用作数组索引。

类型bool可以转换为值为false的int,true变为1。

因此,!ParameterWorkingIdx等同于ParameterWorkingIdx == 0 ? 1 : 0,这更加清晰易懂。


通过这样的编写方式,您的代码不会生成分支指令,尽管其直接目的可能不明显... - XapaJIaMnu
@XapaJIaMnu 我们编写的代码是为了让人类阅读,而不仅仅是为了计算机。 :) - songyuanyao
2
如果这个代码块的性能非常重要,避免分支指令会有所帮助,那么可以将三元条件版本作为注释添加进去。或者使用同样含义的通俗语言描述。 - Vivian
我的意思是,当直接目的不明显时,应该通过注释使其变得明显。就像@David Heyman所说的那样。 - XapaJIaMnu
在这种情况下,应该在源代码中放置清晰明确的文档,解释为什么需要调用特殊类型的语言魔法。 - code_dredd
对于可读版本(ParameterWorkingIdx == 0 ? 1 : 0),clang使用cmove指令而不是分支(x86_64)。gcc则会分支。 - tamas.kenez

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