位运算

 位运算我想我们每个人都会了,任意给你两个数,让你对它们进行位运算,我想你肯定也能把它轻松搞定。但是若是让你把它轻松的运用到编程中来, 我想就没那么容易了.我想即使是工作多年的程序员也未必能把位运算用的很好,运用自如,我感觉位运算里面的东西太多了,想把位运算灵活的运用好,运用的巧 妙真的很难。所以这里先把自己学的一些位运算的知识做个小总结,以后再学到了再总结.
       对于位运算的运算法则就不在总结了,我想这些一旦学了都是一辈子也忘不了的东西.
1: 按位与 (&)
功能:
①: 对某些位清零.
       对某些位清零,我们就使用0与这些为进行相与.在与运算中,0 与任何位相与都是0.
②: 保留某些位.
       使用1与这些位相与.在与运算中,1与任何位相与都是那些位本身.
2: 按位或 ( | )
功能:
①: 保留某些位.
   使用0与这些位相或.在或运算中,0与任何位相或都是那些位本身.
②: 对某些位置1.
       使用1与这些位相或,在或运算中,1与任何位相或都是1.
3: 按位异或 ( ^ )
功能:
①: 对某些位取反.
       在异或运算中,使用1与某一位进行异或可以达到取反的功能.
       如: 10101011.
10101011 ^ 11111111 = 01010100.
②: 保留某些位
       在异或运算中,使用0与某一位进行异或可以保持该位不变.
如:
10101011 ^ 00000000 = 10101011.
对于异或的应用有一个经典的广为流传的例子:
对a,b两个变量的值对调.
有三种方法,其他两种就不在多说,这里只说使用异或的方法:
a = a ^ b;
b = a ^ b;
a = a ^ b;
很多同学看了这个例子,感觉很牛,但有些一同学并没有理解其中的道理和规律.只是死记下来了,这样对学习并不好.我们看一下这三句代码.
首先我说一下我总结的规律: 任何三个数,只要其中两个的异或为第三个,那么这三个数任意两个的异或就是第三个.
好的,我们就一起看这三句代码来验证这个规律把.
为了清楚我们引进第三个变量c.
c = a ^ b; //满足了我规律的条件,那么就可以使用我规律的结论了.
è a = c ^ b;
è b = c ^ a;
好的,现在对比我们的结论和上面的三句代码:
a = a ^ b; è c = a ^ b; a = c;
b = a ^ b; è b = c ^ b; è b = a;
a = a ^ b; è a = c ^ a; è a = b;
OK,看看是不是很简单,很好理解啊.遇到异或使用我的规律没问题.
4: 取反 (~)
功能:
       对某一数字进行取反操作.即二进制位0变成1,1编程0.
5: 左移 ( << )
功能:
       对某一数字的二进制整体向左移位.
6: 右移 ( >>)
功能:
       对某一数字的二进制整体向右移位.
好多同学对左移右移都记住这样一个规律,向左移一位相当于该数乘以2,向右移动一位相当于该数除2.是这个规律吗?
如:
133 è 10000101
我们对其向左移动一位 è 00001010 = 10 由133 è 10.不符合规律吧.
对于数字10 è 00001010
我们在对此数向右移动一位 è 00000101 = 5.好,符合上面的规律,我们在对其向右移动一位00000101 è 00000010 = 2 .不符合规律了吧?呵呵.
由此我们便可以得出了,只有移位时,移出的位为不包含1时才符合上面的规律.
其实我们上面的方法并不准确,因为我们都知道数据在计算机中都是以二进制进行存储的,并且都是以补码的形式进行存储的.即我们所做的移位也是对 其补码进行移位,说到这里我们不得不提出两个概念:
算数移位
逻辑移位
对于左移: 算数左移 和 逻辑左移是一个概念都是右边补0.
对于右移: 对于无符号数,算数右移和逻辑右移也是相同的都是左边补0.对于有符号数,算数右移和逻辑右移就有所不同了,如果在左边补1,我们称为算数右移,如果在左 边补0,我们称为逻辑右移.
由于位运算是对内存的直接操作,所以速度是非常快的,所以在很多对速度要求很高的算法中被大量使用.
下面我们看一些小例子来练习一下(这些例子大部分来自《代码揭秘》):
1 请实现一个判0的函数,如果参数为0,则返回1,否则返回0.
作者的答案:
int isZero(int x)
{
       int xZero = x | 0;
       return !xZero;
}
作者使用了或运算的保留某些位的功能,即与0或不改变原值.
但我认为完全没有必要.直接使用return !x; 就OK了.完全没有必要为了使用位运算而使用位运算.不过作者肯定是为了想让大家理解位运算才这样做的.
2 请实现一个判等函数,相等返回1.否则返回0.
作者的答案:
int isEqual(int a,int b)
{
       return !(a ^ b);
}
这个小程序使用了异或的运算。运用的很好,如果两个数相等的话,那么他们的每一位肯定也都相同,那么对它们进行异或运算结果肯定为0.所以
if (a == b) è a ^ b == 0;
3 请实现一个判正数函数,如果参数是整数,返回1,否则返回0.
作者的答案:
int isPositive(int a)
{
       return !((x >> 31) + !a)
}
这段代码没有看懂,估计是作者的笔误,写错了.那我们自己想答案吧:
判断一个数是否为正数,我们只需判断它的最高位是否为1就可以了.那如何判断一个位是否为1呢?这时候我们想起了与运算的功能,1只有与1相与 为1.所以我们开始我们的旅程吧(sizeof(int)*8 = = 32位):
!((1 << 31) & a)
1 << 31 = 10000000000000000000000000000000.
如果a为正数或0,(1 << 31) & a 的结果为0,否则为1.
所以我们应该返回 !((1 << 31) & a)
int isPositive(int a)
{
       return !((1 << 31) & a);
}
看这个函数是不是不太好啊,太没有可移植性了.我们来对其做些修改:
int isPositive(int a)
{
       return !((1 << (sizeof(int)*8 – 1)) &a);
}
OK,搞定.
4 在32位系统下,如果有一个4字节的数据x,这些字节从低到高标记为0~3,现在要求从该数据中抽取出第n个字节。
作者答案:
int getByte(int x,int n)
{
       return (x >> (n << 3)) & 0xff;
}
好的,开始思索.① 抽取出第n字节,即保留第n字节.想想具有保留功能的是哪一个位运算?
其实& , | , ^ 都具有保留功能。但是我们只能使用 & 运算原因可想而知,因为我们不但要保留该字节,还要把其他的清零.虽然^也有清零功能,但是必须找到相同的数字.用起来比较麻烦. ② 我们该如何获取第n个字节呢.其实我们只需把这个字节放到一个比较方便进行位运算的地方就OK了,那么什么地方最方便呢,当然是最低字节了.
好的,思路明确了,将目的字节放到最低位置,然后进行位运算.
如果n是第0字节,那么我们无需移动。
如果n是第1字节,我们只需向右移动8位。
如果n是第2字节,我们只需向右移动 16位.
如果n是第3字节,我们只需向右移动 24位.
好的:
int getByte(int x,int n)
{
       return (x >> (n * 8)) & 0xff;
}
OK,比较一下我们和作者的程序的区别,显然我们使用了乘法运算效率比较低.我们只需将
n * 8 改为: n << 3 就与作者的答案一样了.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值