前几天,我试图使用 SDL 多媒体库编写一个小C++编程,遇到了这个小障碍,最终通过反复试验解决了这个问题。问题是,我明白我为解决问题做了什么,但我并不真正了解问题的本质!
问题出在 SDL 中的键盘事件处理上。处理单键退出程序的代码简单明了。[事件队列是一个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"成员时,有必要使用"&"按位AND运算符。
现在,如果我不得不猜测,我会说这与"keysym.sym"只需要处理代表键盘上单个键的单个数值有关,而"keysym.mod"必须处理shift,ctrl和alt键的各种组合......?
总结一下我的问题:为什么会这样?除了反复试验之外,是否有其他需要学习的某个数据是否需要与按位或逻辑/相等运算符进行比较?为什么"keysym.sym == SDLK_F4"工作正常,而"keysym.mod == KMOD_ALT"却不能?为什么涉及十进制数的操作与比较位值的操作具有不同的结果?是否存在逻辑运算有效而按位运算不起作用的情况?
按位 AND 有点特殊。 ==
检查相等性,但按位 AND 运算符允许您使用数字的各个位。
假设您的事件被定义为键列表:
event = ['a', 'shift', 'ctrl']
然后,您可以非常轻松地检查特定修饰符是否是事件的一部分:
if 'shift' in event:
# ...
按位 AND 有点像in
语句。您可以将事件定义为二进制数,如下所示:
event = 00010010
现在,当您执行按位 AND 时,您可以轻松检查是否已将某个修饰符应用于事件,因为修饰符也表示为二进制数:
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)
----------
维基百科很好地总结了按位运算:
按位运算在其各个位级别对一个或多个位模式或二进制数字进行操作。它是处理器直接支持的快速、原始操作,用于操作值以进行比较和计算。在简单的低成本处理器上,按位运算通常比除法快得多,比乘法快几倍,有时比加法快得多。虽然现代处理器执行加法和乘法的速度通常与按位运算一样快,因为它们的指令流水线较长,其他体系结构设计选择,但按位运算通常使用较少的功率/性能,因为资源使用减少。
基本上,按位运算符允许您有效地处理存储在整数位中的信息。
你在这里做了什么
eventQueue.key.keysym.mod & KMOD_ALT
不是比较操作,而是位屏蔽操作。比较操作在 C 和 C++ 中是隐式的:计算结果为零的表达式表示"false",所有非零值表示"true"。当在像您这样的逻辑表达式中使用时,这是
(eventQueue.key.keysym.mod & KMOD_ALT) != 0
现在来看位运算:某些值表示两个或多个值的位组合。例如,keysym.sym
表示 ALT 的位模式(它本身是左 ALT 和右 ALT 的组合)和可能同时按下的任何其他键的组合。为了将一个值与组合分开,使用位掩码技术:在感兴趣的位中具有 1 的值在所有其他位(即 KMOD_ALT
)中具有 1 的值与组合值(在您的情况下,它是 keysym.sym
的)一起进行 AND-ed,在由 KMOD_ALT
的 1
s 表示的位中产生keysym.sym
位。
最终结果是,只有按下 ALT 时,eventQueue.key.keysym.mod & KMOD_ALT
才会不为零。
免责声明:我对 SDL 几乎一无所知。我在这里回答的主要是猜测。
键盘上有许多键,无论按多少其他键,它们都会生成键事件。Shift、Alt、Ctrl 等修饰键就是这样的键(不确定是否还有更多),键盘制造商必须确保可以同时按下它们。其余键是普通键,如果同时按下,可能会也可能不会生成键事件,具体取决于每个键盘的电路。
按下普通键时,将触发键事件(不确定按修饰键是否会触发事件)。正常键处于sym
状态,按下普通键时是否按下任何修饰键记录在 mod
中。我很确定,在实现方面,mod
中的某些位用于定义是否按下了某些修饰键。要检查该位是否打开,您需要使用常量进行位&
,该常量定义使用哪个位来指示是否按下修饰键。