导言:这里位运算中的位是指二进制位,所以位运算都是二进制数值之间的运算。不过在脚本中也可以看到十进制数进行位运算,实际情况是脚本解释器会自动将其转换为二进制数并计算出结果。
理解二进制
在说明位运算之前,我们需要理解在计算机中是如何存储数字的。最简单的形式是使用开关状态表示,即关闭或打开,在程序中分别表示为 0 和 1(被称为位)。
十进制
二进制只是表示数字的另一种方式。在日常生活中我们使用十进制,其中有个位、十位、百位等。从技术上来说,每个位的值等于这个位的数字乘以 10^n,这里 n 表示到右边的距离,从 0 开始。因此,数字 647 等于 (6 * 100) + (4 * 10) + (7 * 1) 或 (6 * 10^2) + (4 * 10^1) + (7 * 10^0)(是的,10^0 为 1)。
二进制
二进制使用相同的原理。每个位的值等于这个位的数字乘以 2^n,这里 n 表示到右边的距离,从 0 开始。因此,数字 1101001 等于 (1 * 64) + (1 * 32) + (0 * 16) + (1 * 8) + (0 * 4) + (0 * 2) + (1 * 1)。如果换算为十进制,则为 105。
位运算
那么在二进制中 1+1 等于多少呢?当然不会是 2,二进制中没有数字 2!结果是 10,它表示 (1 * 2^1) + (0 * 2^0)。
所有的基本算术运算(加、减、乘、除)都与十进制工作方式相同。所以在这些运算中,我们没有理由使用二进制而不用十进制。然而,还有其他一些运算是二进制独有的:按位与、按位或、按位异或、按位非、按位左移和按位右移。
按位与
按位与(在 AutoHotkey 中使用单个 & 符号表示)需要两个数参与运算。运算时对每个同一位置的位进行如下操作:如果两个位都为 1,则结果为 1,否则为 0。请看示例:
Code: Select all
1100
&1010
_____
1000
按位或
按位或(在 AutoHotkey 中使用单个 | 符号表示)也需要两个数参与运算。按位进行如下操作:只要其中有一个位为 1,则结果为 1,否则为 0。例如:
Code: Select all
1100
|1010
_____
1110
按位非
按位非(AutoHotkey 中表示为 ~)只需要一个操作数。它会反转操作数中的每个位,如果这个位为 0,则反转为 1,否则反转到 0。例如:
Code: Select all
~1010
_____
0101
Code: Select all
~0001
_____
1110
按位异或(注:AutoHotkey 中异或的符号为 ^,而 ** 表示乘方)需要两个操作数。按位进行如下操作:如果两个位不同,则结果为 1,否则为 0。换种说法是,仅某个位为真,结果才为真,否则为假(这也是异或术语的由来)。例如:
Code: Select all
1010
^1100
_____
0110
按位左移(<<)和按位右移(>>)只需要一个操作数。操作时,把位向左或向右进行移动。例如:
Code: Select all
00001010 << 3
_____________
01010000
Code: Select all
0011100 >> 2
____________
0000111
Code: Select all
0101 >> 1
_________
0010
Code: Select all
0011 (二进制表示的 3)
>> 1 (右移 1 位)
____
0001 (二进制表示的 1)
位赋值运算符看起来有点奇怪,例如 >>=、^= 和 |=。这些运算符的左边是变量,右边是参数,把它们连在一起进行计算(在等号前的运算符指定运算的类型),计算出结果后保存回变量中。例如:
Code: Select all
MyVar := 5 ; 二进制的 101
MyVar |= 2 ; 101 | 010 = 111(二进制)= 7(十进制)
MsgBox % MyVar ; 7
MyVar := 5 ; 二进制的 101
MyVar <<= 2 ; 101<<2 = 10100(二进制)= 20(十进制)
MsgBox % MyVar ; 20
现在您已经了解了二进制的基本知识和它能进行的运算,可以用在哪里呢?在普通的脚本中可能用的不多,不过在 WinAPI 和需要传递给它的结构之间就很常用了。
在消息中有时我们会遇到同时包含了多种信息的参数,此时需要分离出来。
例如在 WM_COMMAND 的回调函数中 wParam 参数的高字和低字各表示一部分信息,我们需要首先分离后才能判断:
Code: Select all
LOWORD := wParam & 0xffff
HIWORD := wParam >> 16
LOWORD := NumGet(wParam, 0, "short")
HIWORD := NumGet(wParam, 2, "short")
还有些意想不到的地方使用后也会方便许多,例如 MsgBox 命令。因为每个选项只有两种状态:打开或关闭,都是 2 的倍数,因此(除了选项为 0 时)每个选项的二进制数字表示形式中都只有一个 1。这样就可以使用按位或把它们加在一起(有些选项数值较大相加不便),例如:
Code: Select all
MsgBox, % 4|16, title, text
Code: Select all
If (VarContaining35 & 32)
; 添加“?”图标