声明:位运算根据不同的操作系统得出的结果可能不同,在此我是根据 8位机 来做的介绍
优先级:! > 算术运算符 > 关系运算符 > && > || > 赋值运算符
一、位运算:
位运算的运算分量只能是整型或字符型数据,位运算把运算对象看作是由二进位组成的位串信息,按位完成指定的运算,得到位串信息的结果。
常用的位运算有 AND(&)、OR(|)、NOT(~)、EXCLUSIVE-OR(异或^)、同一运算(I 大写的i)、移位运算(左移运算 << 、右移运算 >>)
位运算符的优先级从高到低,依次为~、&、^、|, 其中~的结合方向自右至左,且优先级高于算术运算符,其余运算符的结合方向都是自左至右,且优先级低于关系运算符。
二、介绍运算(转换成2进制位进行运算)
1. AND(&) 按位与运算(将两个运算分量的对应位按照一定的规则就行运算):
规则:相同位置的位的值都是1,则结果为1,否则即为0
比如:1 & 0 、3 & 2 、4 & 2 、6 & 2
转换2进制:00000001 & 00000000(1 & 0)、00000011 & 00000010(3 & 2)、00000100 & 00000010(4 & 2)、00000110 & 00000010(6 & 2)
换种形式:00000001、00000011、00000100、00000110
00000000、00000010、00000010、00000010
结果:1 & 0 = 0、3 & 2 = 2、4 & 2 = 0、6 & 2 = 2
按位与运算就是集合的交集,很简单,A = {0,2,3,6}, B = {0,1,4,6},那么A与B的交集就是{0,6}
2. OR(|)按位或运算(将两个运算分量的对应位按照一定的规则就行运算):
规则:相同位置的位的值只要有1个值是1,结果就是1,否则即为0
比如:1 | 0 、2 | 1 、6 | 2 、8 | 3
转换2进制:00000001 | 00000000(1 | 0)、00000010 | 00000001(2 | 1)、00000110 | 00000010(6 | 2 )、00001000 | 00000011(8 | 3)
换种形式:00000001 、00000010 、00000110 、00001000
00000000 、00000001 、00000010 、00000011
结果:1 | 0 = 1、2 | 1 = 3、6 | 2 = 6、8 | 3 = 11
按位或运算就是集合的并集,根据规则就可以看得出了,比如10000001 | 011111110,那结果就是这两个二进制数的合并,也就是11111111
3. EXCLUSIVE-OR(异或^)按位异或运算(将两个运算分量的对应位按照一定的规则就行运算):
规则:相同位置的位的值相同的,结果为0,不相同的结果为1
比如:1 ^ 2、0 ^ 0、3 ^ 6、9 ^ 1
转换2进制:00000001 ^ 00000010(1 ^ 2)、00000000 ^ 00000000(0 ^ 0)、00000011 ^ 00000110(3 ^ 6)、00001001 ^ 00000001(9 ^ 1)
换种形式:00000001 、00000000 、00000011 、00001001
00000010 、00000000 、00000110 、00000001
结果:1 ^ 2 = 3、0 ^ 0 = 0、3 ^ 6 = 5、9 ^ 1 = 8
4. NOT(~)按位取反运算(按位取反运算是单目运算,用来求一个位串信息按位的反)
规则:二进制位的值,为1的结果是0,为0的结果是1
比如: ~7 、~9、~0、~10
转换2进制:00000111(7)、00001001(9)、000000(0)、00001010(10)
转换结果: 11111000(~7)、11110110(~9)、11111111(~0)、11110101(~10)
结果(16进制,10进制懒得算):~7 = 0xf8、~9 = 0xf6、~0 = 0xff、~10 = 0xf5
按位取反运算就是“补运算”了,通过规则,比如:10001110 补运算的结果是:01110001,把二进制数和其补运算的结果合并在一起刚好是11111111
5. identity operation (I)同一运算:
规则:
(1). 如果两个值类型不相同,则它们不相等
(2). NaN和其他任何值都是不相等的,包括它本身
(3). 如果两个引用值指向同一个对象,数组或函数,则它们是相等的。如果指向不同的对象,则它们是不等的,尽管两个对象具有完全一样的属性
如果不相同,则无法进行其他操作
比如:a ^ I (a) = a ^ a = 0;
6. 移位运算(移位运算是双目运算,有两个运算分量,左分量为移位数据对象,右分量的值为移位位数。移位运算将左运算分量视作由二进位组成的位串信息,对其作向左或向右移位,得到新的位串信息,移位运算符的优先级低于算术运算符,高于关系运算符,它们的结合方向是自左至右。)
1. 左移运算符(<<):
规则:将一个位串信息向左移动指定的位置,右端空出的位补0,左端溢出的高位舍弃,且
比如:-2 << 4
过程:-2 先转换成二进制(2进制的补码),00000010 -补码-> 11111101 + 1 -结果-> 11111110 -根据规则,往左移动4位,右端空出的位补0-> 111111100000 -根据系统位数8位机,左端溢出的高位要被舍去,结果保留8位-> 11100000 -转换成10进制是224,这是补码的结果,我们在逆运算转回去,这是负数,前面加上负号-> -00011111 - 结果是 -31,再减去1 -> -31-1 = -32
结果:-2 << 4 = -32; (可能大家很疑惑2进制的补码怎么算,这里给个简单的公式,先把二进制取反,在加1,也就是先~按位取反操作再加上1,-2 = 00000010 = 11111101 + 1 = 11111110 = 254)
比如:2 << 4
过程:无符号整数就好算多了, 2 -二进制-> 00000010 - 根据规则往左移动4位,其实也就是在二进制的右端加4个0 -> 000000100000 - 根据系统位数8位机,高位要舍去,保留8位二进制数 -> 00100000- 转换成10进制 -> 32
2. 右移位运算(>>):
规则(分有符号数和无符号数):将一个位串信息向右移动指定的位置,右端溢出的位舍去
1> 无符号右移:左端空出的位补0
2> 有符号右移:
移位前符号为0(正数):左端空出的位也补0;
移位前符号为1(负数):左端用0或者1补充,取决于计算机系统;负数右移补充0,称为“逻辑右移”;负数右移补充1,称为“算术右移”;
可以使用以下方法来测试自己系统针对负数右移的具体方法:printf("%d ", -2 >> 4);
无符号右移示例:
比如:3 >> 3
过程: 00000011 - 根据规则往右移动三位,左侧补充0 -> 00000000011 -右侧溢出舍去 -> 00000000 - 转换 -> 0
结果: 3 >> 3 = 0;
有符号右移:
正数:跟上边无符号右移一样,不做演示
负数:(上边说过负数如何转成2进制数表示,就是2进制的补码,先按位取反运算,再加1)
比如:-2 >> 4
过程:00000010 - 取反 -> 11111101 + 1 - 结果-> 11111110 ,计算到这里根据系统不同可能是“逻辑右移”,可能是“算术右移”
逻辑右移:11111110 - 负数右移,左侧补充0为逻辑右移 -> 000011111110 - 根据系统位数8位,根据右移规则,右端溢出舍去,从左往右数8位,便是结果 -> 00001111 - 结果 -> 15
逻辑右移结果:-2 >> 4 = 00001111(15) - 转换回2进制补码 -> -(11110000) -1 - 结果 -> -241
算术右移:11111110 - 负数右移,左侧补充1为算术右移 -> 111111111110 - 根据系统位数8位,根据右移规则,右端溢出舍去,从左往右数8位,便是结果 -> 11111111 - 结果 -> 255
算术右移结果:-2 >> 4 = 11111111(255) - 转换回2进制补码 -> 00000000 -1 - 结果 -> -1
三、题目
1.只使用位级和逻辑运算,编写一个C表达式,他等价于x==y。换句话说,当x和y相等时他将返回1,否则返回0
答:因为 x ^ y 只会在x == y时为0,所以我们可以利用这一性质得到这个表达式其实就是 !(x ^ y)