按位取反
详细了解按位取反。
位运算 会单独处理数字的每一位。我们在编程时经常使用它们。下面我们详细探讨它们的工作原理。
位取反
首先,我们来看最简单的位运算:取反。它也被称为位非(bitwise NOT)。在 Bash 中,使用波浪号符号(~)表示该操作。
进行位取反时,我们会交换整数每一位的值。也就是说,将每个 1 变为 0,每个 0 变为 1。
以下是以数字 5 为例进行位非运算的示例:
15 = 101 2~5 = 010
从数学角度看,位取反是一个简单操作。但在编程中使用时需要注意一些陷阱。我们需要牢记两点:
- 数字占用多少字节?
- 数字在内存中的表示方式是什么?
假设一个两字节变量存储数字 5,那么它在内存中的表示如下:
100000000 00000101
对该变量执行位取反后,结果为:
111111111 11111010
这些位代表什么?如果将其存储为无符号整型变量,则得到数值 65530。如果变量是有符号整型,则等于 -6。我们可以通过将补码表示转换为十进制来验证这一点。
Bash 的不同内置命令和运算符对整数的表示方式不同。例如,echo 始终以有符号整数形式输出数字。而 printf 命令允许我们选择有符号或无符号整数。
Bash 语言本身没有类型。所有标量变量都以字符串形式存储。在参与算术表达式之前,Bash 会将字符串转换为整数。整数的解释方式(有符号或无符号)取决于上下文。
Bash 中允许的最大和最小整数
Bash 为每个整数分配 64 位内存空间,无论其符号如何。下表展示了 Bash 中允许的最大和最小整数。
| 整数类型 | 十六进制 | 十进制 |
|---|---|---|
| 最大正有符号整数 | 7FFFFFFFFFFFFFFF | 9223372036854775807 |
| 最小负有符号整数 | 8000000000000000 | -9223372036854775808 |
| 最大无符号整数 | FFFFFFFFFFFFFFFF | 18446744073709551615 |
以下示例展示了 Bash 在 (( 运算符、echo 和 printf 内置命令中如何解释整数:
1$ echo $((16#FFFFFFFFFFFFFFFF)) 2-1 3 4$ printf "%llu\n" $((16#FFFFFFFFFFFFFFFF)) 518446744073709551615 6 7$ if ((18446744073709551615 == 16#FFFFFFFFFFFFFFFF)); then echo "ok"; fi 8ok 9 10$ if ((-1 == 16#FFFFFFFFFFFFFFFF)); then echo "ok"; fi 11ok 12 13$ if ((18446744073709551615 == -1)); then echo "ok"; fi 14ok
上述最后一个比较示例(18446744073709551615 与 -1)证实了 Bash 在内存中以相同方式存储有符号和无符号整数,但根据上下文进行不同的解释。
回到数字 5 的位取反运算。Bash 返回的结果是十六进制的 0xFFFFFFFFFFFFFFFFFA。我们可以将其以正数或负数形式打印出来,如下所示:
1$ echo $((~5)) 2-6 3 4$ printf "%llu\n" $((~5)) 518446744073709551610
在 Bash 中,数值 18446744073709551610 和 -6 是相等的。这是因为它们在内存中的所有位都完全相同。