注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。
测试环境:Ubuntu 10.10
GCC版本:4.4.5
一、位运算符分析
1)C语言中的位运算符
位运算符直接对bit位进行操作,其效率最高。
2)左移和右移注意点
- 左操作数必须为整数类型
*char 和 short被隐式转换为int后进行移位操作
- 右操作数的范围必须为:[0,31]
- 左移运算符<<将运算数的二进制位左移
*规则:高位丢弃,低位补0
- 右移运算符>>把运算符的二进制位右移
*规则:高位补符号位,低位丢弃
二、有趣的问题
1)0x1<<2 + 3的值会是什么?
分析:考察的是优先级问题
A同学:先算0x1 << 2 再把中间结果为3,最终为7。
B同学:我觉得先算2 + 3,所以结果为32。
C同学:可以这么混合计算吗?
问题:
原作者的本意究竟想表达什么?
实例分析
位运算符初探
16-1.c
#include <stdio.h>
int main()
{
printf("%d\n", 3 << 2); //11<<2 ==> 1100 ==> 12
printf("%d\n", 3 >> 1); //11>>1 ==> 01 ==> 1
printf("%d\n", -1 >> 1); //-1==>补码:0xffff,右移1位:0xffff==>-1
printf("%d\n", 0x01 << 2 + 3); //0x01<<(2 + 3) = 32
//printf("%d\n", 3 << -1); // oops! gcc显示1
return 0;
}
操作:
1) gcc 16-1.c -o 16-1.out编译正确,打印结果:
12
1
-1
32
分析:
数学运算符优先级高于逻辑运算符。
小贴士
防错准则:
- 避免位运算符,逻辑运算符和数学运算符同时出现在一个表达式中
- 当位运算符,逻辑运算符和数学运算符需要同时参与运算时,尽量使用括号()来表达计算次序
小技巧:
左移n位相当于乘以2的n次方,但效率比数学运算符高
右移n为相当于除以2的n次方,但效率比数学运算符高
编程实验
交换两个整型变量的值
16-2.c
#include <stdio.h> //只从IDE中找头文件
/*
借助一个变量为中间值,进行两个数值交换
*/
#define SWAP1(a,b) \
{ \
int t = 0; \
t = a; \
a = b; \
b = t; \
}
/*
用加减方式进行数据交换(如果两个整数都很大,这种方式会导致数据溢出)
*/
#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; \
}
int main(int argc , char* argv[])
{
int a = 1;
int b = 2;
printf("a = %d\n" , a);
printf("b = %d\n" , b);
//SWAP1(a , b);
//SWAP2(a , b);
SWAP3(a , b);
/*
SWAP3:
第一行结果:a = (00 00 00 01)^(00 00 00 10) = 11(3)
第二行结果:b = (00 00 00 11)^(00 00 00 10) = 01(a:1)
第三行结果:a = (00 00 00 11)^(00 00 00 01) = 10(b:2)
*/
printf("a = %d\n" , a);
printf("b = %d\n" , b);
return 0;
}
操作:
1) 使用SWAP1(a, b):gcc 16-2.c -o 16-2.out编译正确,打印结果:
a = 1
b = 2
a = 2
b = 1
分析:
数值交换,用变量太普通了。
2) 使用SWAP2(a, b):gcc 16-2.c -o 16-2.out编译正确,打印结果:
a = 1
b = 2
a = 2
b = 1
分析:
使用加减法,要考虑数值类型溢出问题,如果想加时,两个数值过大,相加可能超过数据类型最大范围。
3) 使用SWAP3(a, b):gcc 16-2.c -o 16-2.out编译正确,打印结果:
a = 1
b = 2
a = 2
b = 1
分析:
使用异或没有数值类型溢出现象。
三、位运算与逻辑运算
1)位运算与逻辑运算不同:
- 位运算没有短路规则,每个操作数都参与运算
- 位运算的结果为整数,而不是0或1
- 位运算优先级高于逻辑运算优先级
实例分析
混淆概念的判断条件
16-3.c
#include <stdio.h>
int main()
{
int i = 0;
int j = 0;
int k = 0;
if( ++i | ++j & ++k )
{
printf("Run here...\n");
}
return 0;
}
操作:
1) gcc 16-3.c -o 16-3.out编译问题,打印结果:
Run here...
分析:
位运算没有短路原则,前置++都是计算后才返回数值。
小结
1.位运算符只能用于整数类型
2.左移和右移运算符的右操作数范围必须为[0,31]
3.位运算没有短路规则,所有操作数均会求值
4.位运算的效率高于四则运算和逻辑运算
5.运算优先级:四则运算 > 位运算 > 逻辑运算