在C语言中的位运算符
& 按位与
2&3 相当于 010 & 011 = 010 十进制就是:2
| 按位或
2 | 3 相当于 010 | 011 = 011 十进制就是:3
^按位异或(不同是1)
2 ^ 3 相当于 010 ^ 011 = 001 十进制就是:1
结合律:a&b&c 等价于 (a&b)&c 等价于 a&(b&c)
交换律:a&b 等价于 b&a
“&、|、^”均适合交换律和结合律
左移和右移注意点:
左移运算符<<将运算数的二进制位左移
规则:高位丢弃(高位移动出界的丢弃,少的补零),低位补0
1<<2 : 0001变成0100
右移运算符>>把运算数的二进制位右移
规则:高位补符号位(正补0填充高位,负补1填充高位),低位丢弃(
低位出界的丢弃对吗?)
8>>3: 0000 1000变成0000 0001
-8>>3: 1111 1000变成1111 1111
取反:~8 :从1000变成0111
易错:(优先级问题)
0x1<<2+3是先算2+3,再算左移,最后32
(0x1<<2)+3 = 7
防错准则:
- 避免位运算符,逻辑运算符和数学运算符同时出现在一个表达式中
- 当位运算符,逻辑运算符和数学运算符需要同时参与运算时,尽量使用括号()来表达计算次序
左移n位相当于乘以2的n次方,但效率比数学运算符高
3<<4 : 0000 0011变成0011 0000 是48,即3×(2的4次方)
右移n位相当于除以2的n次方,但效率比数学运算符高
8>>4 :0 1000变成 0000 0001 是1,即8÷(2的3次方)
交换两个变量分析(逻辑运算符妙用)
#define SWAP1(a,b) \
{ \
int temp = a; \
a = b; \
b = temp; \
}
#define SWAP2(a,b) \
{ \
a = a + b; \
b = a - b; \
a = a - b; \
}//不借用第三个变量,数据较大时不可行,会溢出
#define SWAP3(a,b) \
{ \
a = a ^ b; \
b = a ^ b; \
a = a ^ b; \
}//都是按位运算,不可能溢出,"^"异或;浮点数不行;效率高
位运算题:
有一个数列(2,3,4,5,2,2,2,4,5,3,1,1,1),其中的自然数都是以偶数的形式出现,只有一个自然数出现的次数为奇数次。编写程序找出这个自然数。(异或,交换律)
#include <stdio.h>
#define DIM(a) (sizeof(a)/sizeof(*a))
int main()
{
int a[] = {2,3,4,5,2,2,2,4,5,3,1,1,1};
int find = 0;
for(int i = 0; i < DIM(a); i++)
{
find = find^a[i];
}
printf("find = %d\n", find);
return 0;
}
思考:
&&, ||, !与&, |, ~的意义是否相同?它们可以在条件表达式中交替使用吗?为什么?
不能交替使用,逻辑运算符和位运算符的语义不同。逻辑运算符考虑操作数的真值(即,它们将操作数视为布尔值,真或假),而位运算符考虑操作数的每一位。因此,在条件表达式中使用逻辑运算符通常更为合适,因为它们直接处理布尔值,这与条件表达式的期望输入相匹配。
1<<32的结果是什么?1<<-1的结果又是什么?为什么?
0000 0001 左移32,溢出,变成0
0000 0001 左移-1,相当于右移1,溢出,变成0
在大多数现代系统上,一个int类型通常是32位的。当尝试将一个32位的整数左移32位时,实际上是在请求一个不可能的操作:将一个32位的数完全移出其表示范围。这种行为是未定义的,因为标准没有规定在这种情况下应该如何处理。在某些编译器和平台上,结果可能是0(因为所有位都被移出了),但这并不是由语言标准保证的。
左移一个负数位同样是未定义行为。位移的数量应该是非负的,因为负位移在逻辑上没有意义。