c语言位运算: C语言中的位运算

=normalfont cellSpacing=0 cellPadding=3 width="95%" border=0>

=normalfont>作者:linux宝库 (http://www.linuxmine.com)
HZQfbaiducukZR0来自:linux宝库 (http://www.linuxmine.com)
HZQfbaiducukZR0联系:linuxmine#gmail.com
HZQfbaiducukZR0
HZQfbaiducukZR0在计算机数据位是可以操作最小数据单位理论上可以用“位运算”来完成所有运算和操作位操作是用来控制硬件或者做数据变换使用但是灵活位操作可以有效地提高运行效率C语言提供了位运算功能 这使得C语言也能像汇编语言样用来编写系统
HZQfbaiducukZR0
HZQfbaiducukZR0  位运算符C语言提供了六种位运算符:
HZQfbaiducukZR0
HZQfbaiducukZR0  & 按位与
HZQfbaiducukZR0  | 按位或
HZQfbaiducukZR0  ^ 按位异或
HZQfbaiducukZR0  ~ 取反
HZQfbaiducukZR0  << 左移
HZQfbaiducukZR0  >> 右移
HZQfbaiducukZR0
HZQfbaiducukZR0  1. 按位与运算 按位与运算符"&"是双目运算符其功能是参与运算两数各对应二进位相与只有对应两个二进位均为1时结果位才为1 否则为0参与运算数以补码方式出现
HZQfbaiducukZR0
HZQfbaiducukZR0  例如:9&5可写算式如下: 00001001 (9二进制补码)&00000101 (5二进制补码) 00000001 (1二进制补码)可见9&5=1
HZQfbaiducukZR0
HZQfbaiducukZR0  按位与运算通常用来对某些位清0或保留某些位例如把a 高八位清 0 保留低八位 可作 a&255 运算 ( 255 二进制数为0000000011111111)
HZQfbaiducukZR0
HZQfbaiducukZR0应用:
HZQfbaiducukZR0a. 清零特定位 (mask中特定位置0其它位为1s=s&mask)
HZQfbaiducukZR0b. 取某数中指定位 (mask中特定位置1其它位为0s=s&mask)
HZQfbaiducukZR0
HZQfbaiducukZR0
HZQfbaiducukZR0  2. 按位或运算 按位或运算符“|”是双目运算符其功能是参与运算两数各对应二进位相或只要对应二个二进位有个为1时结果位就为1参与运算两个数均以补码出现
HZQfbaiducukZR0
HZQfbaiducukZR0   例如:9|5可写算式如下:
HZQfbaiducukZR0
HZQfbaiducukZR000001001|00000101
HZQfbaiducukZR000001101 (十进制为13)可见9|5=13
HZQfbaiducukZR0应用:
HZQfbaiducukZR0常用来将源操作数某些位置1其它位不变 (mask中特定位置1其它位为0 s=s|mask)
HZQfbaiducukZR0
HZQfbaiducukZR0  3. 按位异或运算 按位异或运算符“^”是双目运算符其功能是参与运算两数各对应二进位相异或当两对应二进位相异时结果为1参与运算数仍以补码出现例如9^5可写成算式如下:
HZQfbaiducukZR0
HZQfbaiducukZR000001001^00000101 00001100 (十进制为12)
HZQfbaiducukZR0应用:
HZQfbaiducukZR0a. 使特定位值取反 (mask中特定位置1其它位为0 s=s^mask)
HZQfbaiducukZR0b. 不引入第三变量交换两个变量值 (设 a=a1,b=b1)
HZQfbaiducukZR0目 标 操 作 操作后状态
HZQfbaiducukZR0a=a1^b1 a=a^b a=a1^b1,b=b1
HZQfbaiducukZR0b=a1^b1^b1 b=a^b a=a1^b1,b=a1
HZQfbaiducukZR0a=b1^a1^a1 a=a^b a=b1,b=a1
HZQfbaiducukZR0
HZQfbaiducukZR0
HZQfbaiducukZR0  4. 求反运算 求反运算符~为单目运算符具有右结合性 其功能是对参与运算各二进位按位求反例如~9运算为: ~(0000000000001001)结果为:1111111111110110
HZQfbaiducukZR0
HZQfbaiducukZR0  5. 左移运算 左移运算符“<<”是双目运算符其功能把“<< ”左边运算数各二进位全部左移若干位由“<<”右边数指定移动位数 高位丢弃低位补0 其值相当于乘2例如: a<<4 指把a各二进位向左移动4位如a=00000011(十进制3)左移4位后为00110000(十进制48)
HZQfbaiducukZR0
HZQfbaiducukZR06. 右移运算 右移运算符“>>”是双目运算符其功能是把“>> ”左边运算数各二进位全部右移若干位“>>”右边数指定移动位数其值相当于除2
HZQfbaiducukZR0
HZQfbaiducukZR0  例如:设 a=15a>>2 表示把000001111右移为00000011(十进制3)对于左边移出空位如果是正数则空位补0若为负数可能补0或补1这取决于所用计算机系统移入0叫逻辑右移移入1叫算术右移Turbo C采用逻辑右移
HZQfbaiducukZR0{
HZQfbaiducukZR0 unsigned a,b;
HZQfbaiducukZR0 prf("input a number: ");
HZQfbaiducukZR0 scanf("%d",&a);
HZQfbaiducukZR0 b=a>>5;
HZQfbaiducukZR0 b=b&15;
HZQfbaiducukZR0 prf("a=%d b=%d ",a,b);
HZQfbaiducukZR0}
HZQfbaiducukZR0
HZQfbaiducukZR0  再看例:
HZQfbaiducukZR0
HZQfbaiducukZR0{
HZQfbaiducukZR0 char a='a',b='b';
HZQfbaiducukZR0  p,c,d;
HZQfbaiducukZR0 p=a;
HZQfbaiducukZR0 p=(p<<8)|b;
HZQfbaiducukZR0 d=p&0xff;
HZQfbaiducukZR0 c=(p&0xff00)>>8;
HZQfbaiducukZR0 prf("a=%d b=%d c=%d d=%d ",a,b,c,d);
HZQfbaiducukZR0}
HZQfbaiducukZR0
HZQfbaiducukZR0
HZQfbaiducukZR0
HZQfbaiducukZR0浮点数存储格式:
HZQfbaiducukZR0
HZQfbaiducukZR0浮点数存储格式是符号+阶码(定点整数)+尾数(定点小数)
HZQfbaiducukZR0SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM
HZQfbaiducukZR0即1位符号位(0为正1为负)8位指数位23位尾数位
HZQfbaiducukZR0浮点数存储前先转化成2k次方形式即:
HZQfbaiducukZR0f = A1*2^k + A2*2^(k-1) + ... + Ak +... +An*2^(-m) (Ai = {0, 1}, A1 = 1)
HZQfbaiducukZR0如5.5=2^2 + 2^0 + 2^(-1)
HZQfbaiducukZR0其中k就是指数加127后组成8位指数位
HZQfbaiducukZR05.5指数位就是2+127 = 129 = 10000001
HZQfbaiducukZR0A2A3.....An就是尾数位不足23位后补0
HZQfbaiducukZR0所以5.5 = 01000000101000000000000000000000 = 40A00000
HZQfbaiducukZR0所以对浮点数*2、/2只要对8位符号位+、- 即可但不是左移、右移
HZQfbaiducukZR0
HZQfbaiducukZR0关于unsigned 在位运算上不同下面有个CU上例子描述很清楚:
HZQfbaiducukZR0
HZQfbaiducukZR0[问题]:这个有什么问题吗?
HZQfbaiducukZR0
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0/**
HZQfbaiducukZR0* 本将两个16比特位值连结成为个32比特位
HZQfbaiducukZR0* 参数:sHighBits 高16位
HZQfbaiducukZR0* sLowBits 低16位
HZQfbaiducukZR0* 返回:32位值
HZQfbaiducukZR0**/
HZQfbaiducukZR0long CatenateBits16( sHighBits, sLowBits)
HZQfbaiducukZR0{
HZQfbaiducukZR0long lResult = 0; /* 32位值临时变量*/
HZQfbaiducukZR0
HZQfbaiducukZR0/* 将第个16位值放入32位值高16位 */
HZQfbaiducukZR0lResult = sHighBits;
HZQfbaiducukZR0lResult <<= 16;
HZQfbaiducukZR0
HZQfbaiducukZR0/* 清除32位值低16位 */
HZQfbaiducukZR0lResult &= 0xFFFF0000;
HZQfbaiducukZR0
HZQfbaiducukZR0/* 将第二个16位值放入32位值低16位 */
HZQfbaiducukZR0lResult |= (long)sLowBits;
HZQfbaiducukZR0
HZQfbaiducukZR0 lResult;
HZQfbaiducukZR0}
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0
HZQfbaiducukZR0
HZQfbaiducukZR0[问题发现]:
HZQfbaiducukZR0
HZQfbaiducukZR0我们先看如下测试代码:
HZQfbaiducukZR0
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0
HZQfbaiducukZR0{
HZQfbaiducukZR0 sHighBits1 = 0x7fff;
HZQfbaiducukZR0 sHighBits2 = 0x8f12;
HZQfbaiducukZR0unsigned usHighBits3 = 0xff12;
HZQfbaiducukZR0 sLowBits1 = 0x7bcd;
HZQfbaiducukZR0long lResult = 0;
HZQfbaiducukZR0
HZQfbaiducukZR0prf("[sHighBits1 + sLowBits1] ";
HZQfbaiducukZR0
HZQfbaiducukZR0lResult = CatenateBits16(sHighBits1, sLowBits1);
HZQfbaiducukZR0prf("lResult = %08x ", lResult, lResult);
HZQfbaiducukZR0
HZQfbaiducukZR0lResult = CatenateBits16(sHighBits2, sLowBits1);
HZQfbaiducukZR0prf("lResult = %08x ", lResult, lResult);
HZQfbaiducukZR0
HZQfbaiducukZR0lResult = CatenateBits16(usHighBits3, sLowBits1);
HZQfbaiducukZR0prf("lResult = %08x ", lResult, lResult);
HZQfbaiducukZR0}
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0
HZQfbaiducukZR0运行结果为:
HZQfbaiducukZR0
HZQfbaiducukZR0[sHighBits1 + sLowBits1]
HZQfbaiducukZR0lResult = 7fff7bcd
HZQfbaiducukZR0lResult = 8f127bcd
HZQfbaiducukZR0lResult = ff127bcd
HZQfbaiducukZR0
HZQfbaiducukZR0嗯运行很正确嘛……于是我们就放心在自己中使用起这个来了
HZQfbaiducukZR0
HZQfbaiducukZR0可是忽然有我们无论如何结果都不对!经过n个小时检查和调试最后终于追踪到……CatenateBits16 !?它返回值居然是错!!
HZQfbaiducukZR0
HZQfbaiducukZR0“郁闷!”你说“这个怎么会有问题呢!?”
HZQfbaiducukZR0
HZQfbaiducukZR0可是更郁闷还在后头呢你把输入量作为参数个简单里面单步调试:
HZQfbaiducukZR0
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0
HZQfbaiducukZR0{
HZQfbaiducukZR0 sHighBits1 = 0x7FFF;
HZQfbaiducukZR0 sHighBits2 = 0x8F12;
HZQfbaiducukZR0unsigned usHighBits3 = 0x8F12;
HZQfbaiducukZR0
HZQfbaiducukZR0 sLowBits1 = 0x7BCD; //你实际使用参数
HZQfbaiducukZR0 sLowBits2 = 0x8BCD; //你实际使用参数
HZQfbaiducukZR0
HZQfbaiducukZR0long lResult = 0;
HZQfbaiducukZR0
HZQfbaiducukZR0prf("[sHighBits1 + sLowBits1] ";
HZQfbaiducukZR0
HZQfbaiducukZR0lResult = CatenateBits16(sHighBits1, sLowBits1);
HZQfbaiducukZR0prf("lResult = %08x ", lResult, lResult);
HZQfbaiducukZR0
HZQfbaiducukZR0lResult = CatenateBits16(sHighBits2, sLowBits1);
HZQfbaiducukZR0prf("lResult = %08x ", lResult, lResult);
HZQfbaiducukZR0
HZQfbaiducukZR0lResult = CatenateBits16(usHighBits3, sLowBits1);
HZQfbaiducukZR0prf("lResult = %08x ", lResult, lResult);
HZQfbaiducukZR0
HZQfbaiducukZR0prf(" [sHighBits1 + sLowBits2] ";
HZQfbaiducukZR0
HZQfbaiducukZR0lResult = CatenateBits16(sHighBits1, sLowBits2);
HZQfbaiducukZR0prf("lResult = %08x ", lResult, lResult);
HZQfbaiducukZR0
HZQfbaiducukZR0lResult = CatenateBits16(sHighBits2, sLowBits2);
HZQfbaiducukZR0prf("lResult = %08x ", lResult, lResult);
HZQfbaiducukZR0
HZQfbaiducukZR0lResult = CatenateBits16(usHighBits3, sLowBits2);
HZQfbaiducukZR0prf("lResult = %08x ", lResult, lResult);
HZQfbaiducukZR0
HZQfbaiducukZR0 0;
HZQfbaiducukZR0}
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0
HZQfbaiducukZR0发现结果竟然是:
HZQfbaiducukZR0
HZQfbaiducukZR0[sHighBits1 + sLowBits1]
HZQfbaiducukZR0lResult = 7fff7bcd
HZQfbaiducukZR0lResult = 8f127bcd
HZQfbaiducukZR0lResult = 8f127bcd
HZQfbaiducukZR0
HZQfbaiducukZR0[sHighBits1 + sLowBits2]
HZQfbaiducukZR0lResult = ffff8bcd //oops!
HZQfbaiducukZR0lResult = ffff8bcd //oops!
HZQfbaiducukZR0lResult = ffff8bcd //oops!
HZQfbaiducukZR0
HZQfbaiducukZR0前次还好好次就ffff了?X档案?
HZQfbaiducukZR0
HZQfbaiducukZR0
HZQfbaiducukZR0[X档案真相]:
HZQfbaiducukZR0
HZQfbaiducukZR0注意那两个我们用来当作低16位值sLowBits1和sLowBits2
HZQfbaiducukZR0
HZQfbaiducukZR0已知:
HZQfbaiducukZR0使用 sLowBits1 = 0x7bcd 时返回正确值;
HZQfbaiducukZR0使用 sLowBits2 = 0x8bcd 时中发生X档案
HZQfbaiducukZR0
HZQfbaiducukZR0那么sLowBits1与sLowBits2有什么区别?
HZQfbaiducukZR0
HZQfbaiducukZR0注意了sLowBits1和sLowBits2都是型(而不是unsigned 所以在这里sLowBits1代表个正数值而sLowBits2却代表了个负数值(8即是二进制1000sLowBits2最高位是1)
HZQfbaiducukZR0
HZQfbaiducukZR0再看CatenateBits16
HZQfbaiducukZR0
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0long CatenateBits16( sHighBits, sLowBits)
HZQfbaiducukZR0{
HZQfbaiducukZR0long lResult = 0; /* 32位值临时变量*/
HZQfbaiducukZR0
HZQfbaiducukZR0/* 将第个16位值放入32位值高16位 */
HZQfbaiducukZR0lResult = sHighBits;
HZQfbaiducukZR0lResult <<= 16;
HZQfbaiducukZR0
HZQfbaiducukZR0/* 清除32位值低16位 */
HZQfbaiducukZR0lResult &= 0xFFFF0000;
HZQfbaiducukZR0
HZQfbaiducukZR0/* 将第二个16位值放入32位值低16位 */
HZQfbaiducukZR0lResult |= (long)sLowBits; //注意这句!!!!
HZQfbaiducukZR0
HZQfbaiducukZR0 lResult;
HZQfbaiducukZR0}
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0
HZQfbaiducukZR0如果我们在中用
HZQfbaiducukZR0
HZQfbaiducukZR0prf("sLowBits = %04x ", sLowBits);
HZQfbaiducukZR0
HZQfbaiducukZR0打印传入sLowBits值会发现
HZQfbaiducukZR0
HZQfbaiducukZR0sLowBits = 0x7bcd 时打印结果为
HZQfbaiducukZR0
HZQfbaiducukZR0sLowBits = 7bcd
HZQfbaiducukZR0
HZQfbaiducukZR0而sLowBits = 0x8bcd时打印结果为
HZQfbaiducukZR0
HZQfbaiducukZR0sLowBits = ffff8bcd
HZQfbaiducukZR0
HZQfbaiducukZR0是即使用%04x也打印出8位十六进制
HZQfbaiducukZR0
HZQfbaiducukZR0因此我们看出来了:
HZQfbaiducukZR0
HZQfbaiducukZR0当sLowBits = 0x8bcd时中 "lResult |= (long)sLowBits;" 这句执行会先将sLowBits转换为
HZQfbaiducukZR0
HZQfbaiducukZR00xffff8bcd
HZQfbaiducukZR0
HZQfbaiducukZR0再与lResult做或运算由于现在lResult值为 0xXXXX0000 (其中XXXX是任何值)所以显然无论sHighBits是什么值最后结果都会是
HZQfbaiducukZR0
HZQfbaiducukZR00xffff8bcd
HZQfbaiducukZR0
HZQfbaiducukZR0而当sLowBits = 0x7bcd时中 "lResult |= (long)sLowBits;" 这句执行会先将sLowBits转换为
HZQfbaiducukZR0
HZQfbaiducukZR00x00007bcd
HZQfbaiducukZR0
HZQfbaiducukZR0再与lResult做或运算这样做或运算出来结果当然就是对
HZQfbaiducukZR0
HZQfbaiducukZR0也就是说CatenateBits16在sLowBits最高位为0时候表现正常而在最高位为1时候出现偏差
HZQfbaiducukZR0
HZQfbaiducukZR0[教训:在某些情况下作位运算和位处理时候考虑使用无符号数值——这个时候往往不需要处理符号即使你需要有符号数值那么也应该考虑自行在CatenateBits16前后做转换——毕竟在位处理中有符号数值相当诡异!]
HZQfbaiducukZR0
HZQfbaiducukZR0下面这个CatenateBits16版本应该会好些:
HZQfbaiducukZR0
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0unsigned long CatenateBits16(unsigned sHighBits, unsigned sLowBits)
HZQfbaiducukZR0{
HZQfbaiducukZR0long lResult = 0;
HZQfbaiducukZR0
HZQfbaiducukZR0/* 将第个16位值放入32位值高16位 */
HZQfbaiducukZR0lResult = sHighBits;
HZQfbaiducukZR0lResult <<= 16;
HZQfbaiducukZR0
HZQfbaiducukZR0/* 清除32位值低16位 */
HZQfbaiducukZR0lResult &= 0xFFFF0000;
HZQfbaiducukZR0
HZQfbaiducukZR0/* 将第二个16位值放入32位值低16位 */
HZQfbaiducukZR0lResult |= (long)sLowBits & 0x0000FFFF;
HZQfbaiducukZR0
HZQfbaiducukZR0 lResult;
HZQfbaiducukZR0}
HZQfbaiducukZR0/////////////////////////////////////////////////
HZQfbaiducukZR0
HZQfbaiducukZR0注意其中 "lResult |= (long)sLowBits & 0x0000FFFF;"事实上现在即使我们把CatenateBits16参数(特别是sLowBits)声明为结果也会是对
HZQfbaiducukZR0
HZQfbaiducukZR0如果有天你把只兔子扔给只老虎老虎把兔子吃了第二天把只老鼠扔给它它又吃了那么说明第天你看错了:它本来就是只猫

HZQfbaiducukZR0

Tags:  c语言中 c语言中的指针 c语言中的 c语言位运算

延伸阅读

最新评论

发表评论