按位运算符
前面介绍的 &
和 |
运算符还有一个作用:对数值执行操作。以这种方式使用时,它们处理的是变量中存储的一系列位,而不是变量值,因此它们称为按位运算符。
本节介绍它们和C#语言定义的其他按位运算符。在大多数开发工作中,除了数学应用之外,这个功能都不太常用。因此本节没有列举示例。
下面首先讨论 &
和 |
。第一个操作数中的每个位都与第二个操作数中相同位置上的位进行比较,在得到的结果中,各个位置上的位 如表 4-5 所示
。
|
运算符与此类似,但得到的结果位是不同的,如表 4-6所示。
表4-5 使用&按位运算符
操作数1的位 | 操作数2的位 | & 的结果位 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
表4-6 使用|按位运算符
操作数1的位 | 操作数2的位 | | 的结果位 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
例如,考虑下面代码中的操作:
int result, op1, op2;
op1 = 4;
op2 = 5;
result = op1 & op2;
这里必须考虑 op1
和 op2
的二进制表示方式,它们分别是 100
和 101
。比较这两个表达方式中相同位置上的二进制数字,得出结果,如下所示:
- 如果op1
和op2
最左边的位都是1,result
最左边的位就是1,否则为0。- 如果op1
和op2
次左边的位都是1,result
次左边的位就是1,否则为0。- 继续比较其他的位。
在这个示例中,op1
和 op2
最左边的位都是1,所以 result
最左边的位就是 1。下一个位都是0,第3个位置上的位分别是 1和 0,则 result
第2~3个位都是 0。最后,结果的二进制值是 100
,即结果是 4。以下是这个过程:
1 0 0 4
& 1 0 1 & 5
———————
1 0 0 4
如果使用 |
运算符,将进行相同的过程,但如果操作数中相同位置上的位有一个是 1,其结果位就是 1, 如下所示:
1 0 0 4
| 1 0 1 | 5
———————
1 0 1 5
^
运算符的用法与此相同。如果操作数中相同位置上的位有且仅有一个是 1,其结果位就是 1,如表 4-7 所示
。
表4-7 使用^运算符
操作数1的位 | 操作数2的位 | ^ 的结果位 |
---|---|---|
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
C#中还可以使用一元位运算符 ~
,它将操作数中的位取反,其结果应是操作数中位为 1的,在结果中就是 0,操作数中位为 0的,在结果中就是 1,如表 4-8 所示
。
表4-8 使用~运算符
操作数的位 | ~的结果位 |
---|---|
1 | 0 |
0 | 1 |
.NET 中使用补码存储整数,这使得使用一元运算符~会使结果看起来有点古怪。以 int
类型为例。int
类型是一个 32位的数字,运算符 ~
将对所有 32 位进行操作,知道这一点有助于理解其原理。
例如,数字 5 的完整二进制表示为:
0000 0000 0000 0000 0000 0000 0000 0101
数字 -5 的完整二进制表示为:
1111 1111 1111 1111 1111 1111 1111 1011
实际上,按照补码系统,(-x)定义为(~x + 1)。这个系统在把数字加在一起时非常有用。例如,把 10 和 -5 加起来(即从10中减去5)的二进制表示为:
0000 0000 0000 0000 0000 0000 0000 1010
+ 1111 1111 1111 1111 1111 1111 1111 1011
= 10000 0000 0000 0000 0000 0000 0000 0101
忽略最左端的 1,就得到 5 的二进制表示。像~1 = -2
这样的式子比较古怪,其原因是底层的结构强制生成了这个结果。
某些情况下,本节介绍的这些按位运算符是非常有用的,因为它们可以用变量中的各个位存储信息。例如,颜色可以使用 3 个位来指定红、绿、蓝。可以分别设置这些位,进行以下一种配置,如表 4-9 所示
。
表4-9 颜色二进制表示方案示例
位 | 十进制数 | 含义 |
---|---|---|
000 | 0 | 黑色 |
100 | 4 | 红色 |
010 | 2 | 绿色 |
001 | 1 | 蓝色 |
101 | 5 | 洋红色 |
110 | 6 | 黄色 |
011 | 3 | 青色 |
111 | 7 | 白色 |
假定把这些值存储在一个类型为 int
的变量中。首先从黑色开始,即值为 0 的 int
变量,可以执行如下操作:
int myColor = 0;
bool containsRed;
// Add green bit, myColor now stores 010
myColor = myColor | 2;
// Add red bit, myColor now stores 110
myColor = myColor | 4;
// Check value of red bit
containsRed = (myColor & 4) == 4;
最后一行代码将值 true
赋予 containsRed
,因为 myColor
的 “红色位”是 1。这种技术在高效使用信息时非常有效,特别适合于同时检查多个位的值(对于 int
值,是 32 位)。但是,在单个变量中存储额外信息有更好的方式,即利用第5章讨论的高级变量类型。
除这 4 个按位运算符外,本节还将介绍另外两个运算符,如表 4-10 所示
。
表4-10 位移运算符
运算符 | 类别 | 示例表达式 | 结果 |
---|---|---|---|
>> | 二元 | var1 = var2 >> var3; | 把 var2 的二进制值向右移动 var3 位,就得到 var1 的值 |
<< | 二元 | var1 = var2 << var3; | 把 var2 的二进制值向左移动 var3 位,就得到 var1 的值 |
这些运算符通常称为位移运算符,最好用一个简单示例加以说明:
int var1, var2 = 10, var3 = 2;
var1 = var2 << var3;
结果,var1
的值是 40。具体过程如下:10 的二进制是 1010
,把该数值向左移动两位,得到 101000
,即十进制中的 40。实际上,是执行了乘法操作。每向左移动一位,该数都要乘以 2,所以向左移动两位,就给原来的操作数乘以 4。而每向右移动一位,则是给操作数除以 2,非整余数将会丢失:
int var1, var2 = 10;
var1 = var2 >> 1;
在这个示例中,var1
的值是 5,而下面的代码得到的值是 2:
int var1, var2 = 10;
var1 = var2 >> 2;
大多数代码中都不使用这些运算符,但应该知道有这样的运算符存在。它们主要用于高度优化的代码,在这些代码中,使用其他数学操作的开销就太高了。因此它们通常用于设备驱动程序或系统代码。
位移运算符也有赋值运算符,如表 4-11 所示
。
表4-11 位移赋值运算符
运算符 | 类别 | 示例表达式 | 结果 |
---|---|---|---|
>>= | 一元 | var1 = var2 >> var3; | 把 var1 的二进制值向右移动 var2 位,就得到 var1 的值 |
<<= | 一元 | var1 = var2 << var3; | 把 var1 的二进制值向左移动 var2 位,就得到 var1 的值 |