【问题标题】:Why do certain situations require the use of 'bitwise' operators instead of 'logical' / 'equality' operators?为什么某些情况需要使用“按位”运算符而不是“逻辑”/“相等”运算符?
【发布时间】:2012-08-10 08:12:41
【问题描述】:

前几天,我尝试使用 SDL 多媒体库编写一个小型 C++ 编程,但遇到了这个小问题,我最终通过反复试验解决了这个问题。问题是,我了解我为解决问题所做的工作,但我并不真正了解问题的本质!

问题在于 SDL 中的键盘事件处理。处理单次按键退出程序的代码非常简单明了。 [eventQueue 是一个 SDL_Event 结构]

//checks for keypress events..
if ( eventQueue.type == SDL_KEYDOWN )
{ 
    //note: uses the boolean logical '==' equals operator..
    if ( eventQueue.key.keysym.sym == SDLK_ESCAPE )
    {
        running = false;
    }
}

在上面的代码中,只需按一下ESCAPE键就可以结束主循环并导致程序清理并关闭...

但是... 处理使用 修饰键 (shift/alt/ctrl) 的按键所需的代码无法与“==”运算符一起正常工作。我花了很长时间才发现我需要使用 按位 AND 运算符而不是相等(逻辑?)运算符。

//checks for keypress events..
if ( eventQueue.type == SDL_KEYDOWN )
{ 
    //note: requires the use of the bitwise AND operator..
    if (( eventQueue.key.keysym.mod & KMOD_ALT ) && (eventQueue.key.keysym.sym == SDLK_F4 ))
    {
        running = false;
    }
}

我的困惑来自这样一个事实,当使用 'keysym.sym' 成员时,逻辑运算符 '==' 工作正常,但是,当使用 'keysym.mod' 成员时,有必要使用'&' 位与运算符。

现在,如果我不得不猜测,我会说这与“keysym.sym”只需要处理代表键盘上单个键的单个数字值这一事实有关,而“keysym.sym”只需要处理一个数字值。 mod' 必须处理 shift、ctrl 和 alt 键的各种组合...?

总结一下我的问题:为什么会这样?除了反复试验之外,是否需要学习某条数据是否需要与按位或逻辑/相等运算符进行比较?为什么“keysym.sym == SDLK_F4”工作正常,但“keysym.mod == KMOD_ALT”却不行?为什么涉及十进制数的运算与比较位值的运算的结果不同?是否也存在逻辑运算有效而位运算无效的情况?

【问题讨论】:

  • == 最好称为“等式运算符”。将其称为“逻辑运算符”听起来好像您正在考虑&&(逻辑与运算符)和&(按位与运算符)之间的区别。但是您实际上是在谈论直接相等检查 (==) 和检查是否设置了特定的位子集(使用 &&,可能与 == 结合使用)之间的区别。
  • 逻辑运算符会短路并计算为布尔值,按位运算符计算为整数。

标签: c++ event-handling sdl bitwise-operators boolean-logic


【解决方案1】:

按位与有些特殊。 == 检查是否相等,但按位 AND 运算符允许您处理数字的各个位。

假设您的事件被定义为键列表:

event = ['a', 'shift', 'ctrl']

然后您可以很容易地检查特定修饰符是否是事件的一部分:

if 'shift' in event:
  # ...

按位与有点像in 语句。您可以将事件定义为二进制数,如下所示:

event = 00010010

现在,当您执行按位与时,您可以轻松检查某个修饰符是否已应用于事件,因为修饰符也表示为二进制数:

  00010001  # event (18)
& 00010000  # shift key (8)
----------
  00010000  # you get a non-zero answer, so the shift key is in the event
----------

  00010001  # event (18)
& 00001000  # "z" key (4)
----------
  00000000  # you get zero because the "z" key wasn't a part of the event
----------

您可以使用按位 OR 构造这样的事件:

  00000001  # shift key (1)
| 10100000  # "a" key (160)
----------
  10100001  # resulting event (161)
----------

维基百科对bitwise operations的总结很好:

按位运算在一个或多个位模式或二进制数字的各个位级别上进行操作。它是处理器直接支持的快速、原始的操作,用于操作值以进行比较和计算。在简单的低成本处理器上,按位运算通常比除法快得多,比乘法快几倍,有时比加法快得多。虽然现代处理器由于其较长的指令流水线和其他架构设计选择,通常执行加法和乘法运算的速度与按位运算一样快,但由于资源使用减少,按位运算通常使用较少的功率/性能。

基本上,位运算符允许您有效地处理存储在整数位中的信息。

【讨论】:

  • 优秀的答案。将事件的位模式与按键进行比较的视觉解释使这一点非常清晰易懂!非常感谢您清晰而详细的回答!我现在很清楚。 :D
【解决方案2】:

你在这里做了什么

eventQueue.key.keysym.mod & KMOD_ALT

不是比较操作,它是位掩码操作。比较操作在 C 和 C++ 中是隐含的:计算结果为零的表达式表示“假”,所有非零值都表示“真”。在像您这样的逻辑表达式中使用时,这是

的简写
(eventQueue.key.keysym.mod & KMOD_ALT) != 0

现在到位操作:某些值表示两个或多个值的位组合。例如,keysym.sym 表示 ALT 的位模式(它本身是左右 ALT 的组合)和任何其他可能同时按下的键的组合。为了从组合中分离一个值,使用了一种位掩码技术:一个在感兴趣的位中具有 1 并且在所有其他位中具有 0 的值(即KMOD_ALT)与组合值进行“与”运算(在您的情况下,它是keysym.sym) 在KMOD_ALT1s 指示的位中产生keysym.sym 的位。

最终结果是eventQueue.key.keysym.mod & KMOD_ALT 只有在按下 ALT 时才会为非零。

【讨论】:

  • 另一个很好的答案。对我来说很有意义。我以前从未听说过“位掩码”,但你写的一切都很清楚,对我来说似乎很有意义!非常感谢您的解释! :)
  • a & b 绝对不是a & b != 0 的简写。
  • @CharlesBailey 哦,不是一般情况,而是在 OP 表达式的上下文中。
  • 不,KMOD_ALT != 1 所以括号是必不可少的。 (eventQueue.key.keysym.mod & KMOD_ALT) != 0
  • @CharlesBailey 但不是KMOD_ALT 使它等价,而是在表达式之外存在&&(参见OP)。 C++隐式将&&的操作数转换为bool,相当于在非bool表达式后面加上!= 0
【解决方案3】:

免责声明:我对 SDL 几乎一无所知。我在这里回答的主要是猜测。

无论按下多少其他键,键盘上都有许多键会产生键事件。 ShiftAltCtrl等修饰键就是这样的键(不确定是否还有),键盘制造商必须确保它们可以同时按下。其余按键均为普通按键,同时按下时可能会产生按键事件,也可能不会产生按键事件,具体取决于每个键盘的电路。

当普通键被按下时,会触发一个按键事件(不确定按下修饰键是否会触发一个事件)。普通键在sym 中,并且在按下普通键时是否按下了任何修饰键记录在mod 中。我很确定,在实现方面,mod 中的某些位用于定义是否按下某个修饰键。要检查该位是否打开,您需要逐位& 使用定义哪个位用于指示修改键是否被按下的常量。

【讨论】:

    猜你喜欢
    • 2013-01-30
    • 1970-01-01
    • 2012-07-20
    • 2014-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-27
    • 2019-07-10
    相关资源
    最近更新 更多