c/c++的位操作

来源:互联网 发布:php获取客户端ip 编辑:程序博客网 时间:2024/05/11 05:35

c/c++的位操作
《说明,本文章面向初学者,高手勿看,如有问题需要探讨 请发邮件: xjtufjj@stu.xjtu.edu.cn 》
c/c++是一种系统编程语言,之所以这样说其中一个原因是是因为其提供的位操作的能力。其提供的位操作符直接对应于相关的汇编指令。在现实的编程实践中,合理的使用位操作会大大提高效率。我先以一个例子来开始我们的讲解:
求一个整数的绝对值
乍一看这个问题,我们一位这样的问题太简单了,所以你可能随手会写出这样的程序:
inline int abs(int a){
        return a >= 0?a:-a;
}
^_^,或许此时你正在为你的杰作而沾沾自喜,呵呵这么短小的函数,同时使用了inline,内联之后减少函数调用的开销,厉害啊,我怎么就这么有才呢?
先别急,我们来看一下这个程序有什么问题:我们知道,现代的CPU都是严重依赖流水线的,不然的话他们的频率怎么会做的老高(IBM已经到4.6G了)。intel,amd的86的cpu的流水已经到十几二十级了。所以这个时候,你一旦卡断流水,那损失是相当大的。这个时候你会发现问题的所在了,原来你的程序卡断了流水!!
这个时候你着急了,难道还有不用判断的绝对值函数?有!首先我们来分析整数的表数方式:现代的计算机中,大多数是补码表示的,负数呢就是原码变反加1,正数就是原码!如下:
-1:   00000000 00000000 00000000 00000001
---> 11111111 11111111 11111111 11111110 + 1
     = 11111111 11111111 11111111 11111111
所以:
设x、y为32位有符号整数,其中y=x>>31,则可以使用表达式(x^y)-y计算x的绝对值。由于x>>31是带符号的向右位移,因此,x为负时,y为全1,即y等于-1;x非负时,y为全0,即y等于0。若非负,则x^y等于x,从而(x^y)-y等于x本身;若x为负,则x^y等于x按位取反,而减y等于加一,从而(x^y)-y等于x的绝对值。
inline int abs(int a){
        return (x^(x>>31))-(x>>31);
}
(一).位运算符 & | ^ ~ << >>
        这几个运算符,相信已经很熟悉了,单数使用的时候未必真的用的很好。下面是一个例子,你看看答案是多少?
        int i= 1;
        i = i<<1+2;
        std::cout<<i<<std::endl;
        如果 你的答案是4的话,我建议你再回去好好复习一下运算符的优先级,正确答案是 8;
(二)位域
        你的c语言已经不是初学者了,你已经知道位域的使用了。所以你对于位的操作可能是这样的:
        struct bset{
        unsigned int bit0 :1;
        unsigned int bit1 :1;
        unsigned int          :28;
        unsigned int bit30 :1;
        unsigned int bit31 :1;
        } ;
        unsigned int a = 0;
        bset* bit = (bset*)&a;
        bit->bit0 = 0;
        bit->bit31 = 1;
        cout<<hex<< a <<endl;
        这个时候你会说位操作原来这么简单,哈哈的确位域给我们提供了一种便捷的方式来完成位操作,但是天下没有免费的午餐,上面的这个程序没有可移植性,不信你把这个程序拿到IBM的处理器上试试看,呵呵 位域最大的缺点就是其可移植性较差,此外位域还有多注意事项,请参考相关书籍。
        其实关于位操作,我们可以使用宏来处理,以下是较为常用的宏
        #define maxk1(i) (1u<<i)
        #define maxk0(i) ~(1u<<i)       
        #define set(n,i)   ((n)|mark1(i))
        #define reset(n,i)   ((n)&mark0(i))
        .........
(三)bitset
        bitset是c++标准库中的成员,他的功能十分强大,看看?~
(1):构造
        要使用bitset必须得包含头文件#include<bitset>
        bitset<32>a;
        bitset<32>b(2399);
        bitset<32>c(string("1001"));
        bitset<32>d(string("10011111"),4,3);
        这三个例子说明了三种构造情况,呵呵看看<c++ primer >中怎么描述的:
        bitset<32>a;          a有32位,每位都为0
       
        bitset<32>b(2399)=》bitset<n> b(u);   b是unsigned long型u的一个副本,当用unsigned long值作为bitset对象的初始值时,该值将转化为二进制的位模式。而bitset对象中的位集作为这种位模式的副本。如果bitset类型长度大于unsigned long值的二进制位数,则其余的高阶位置为0;如果bitet类型长度小于unsigned long值的二进制位数,则只使用unsigned值中的低阶位,超过bitet类型长度的高阶位将被丢弃
        bitset<32>c(string("1001")); bitset<n> b(s);  b是string对象s中含有的位串的副本
当用string对象初始化bitset对象时,string对象直接表示为位模式。从string对象读入位集的顺序是从右向左,string对象和bitset对象之间是反向转化的:string对象的最右边字符(即下标最大的那个字符)用来初始化bitset对象的低阶位(即下标为0的位)。当用string对象初始化bitset对象时,记住这一差别很重要。
 bitset<32>d(string("10011111"),4,3);   bitset<n> b(s, pos, n)    b是s中从位置pos开始的n个位的副本
 
(2 )操作:
b.any()
 b中是否存在置为1的二进制位?
 
b.none()
 b中不存在置为1的二进制位吗?
 
b.count()
 b中置为1的二进制位的个数
 
b.size()
 b中二进制位的个数
 
b[pos]
 访问b中在pos处的二进制位
 
b.test(pos)
 b中在pos处的二进制位是否为1?
 
b.set()
 把b中所有二进制位都置为1
 
b.set(pos)
 把b中在pos处的二进制位置为1
 
b.reset()
 把b中所有二进制位都置为0
 
b.reset(pos)
 把b中在pos处的二进制位置为0
 
b.flip()
 把b中所有二进制位逐位取反
 
b.flip(pos)
 把b中在pos处的二进制位取反
 
b.to_ulong()
 用b中同样的二进制位返回一个unsigned long值
 
os << b
 把b中的位集输出到os流

这么多的操作,看花眼了,不用着急,给你个程序,ctrl c  crtl v到你的编译器中仔细看:

 
#include<iostream>
#include<bitset>
#include<limits>
using namespace std;
int main(int argc, char* argv[])
{       
        bitset<numeric_limits<unsigned long>::digits > b(1111); // 构造一个bitset对象
       
        //有没有1 位?
        cout<<"Is any bit set to 1?:/t"<<boolalpha<<b.any()<<endl;
        //所有的位都是0 嘛?
        cout<<"are all bits set to 1?/t"<<boolalpha<<b.none()<<endl;
        //为1的为的个数是多少?
        cout<<"how many bits set to 1?/t"<<b.count()/*size_t*/<<endl;
        //二进制的长度?
        cout<<"how long the obj     ?/t"<<b.size()<<endl;
   
        //get the value of p
        cout<<"get the value of p[0]?/t"<<noboolalpha<<b[0]<<endl;
        //set the bit of p
        cout<<"set the value of p[31]?/t"<<noboolalpha<<b.set(31)<<endl;
        //reset the bit of p
        cout<<"reset the value of p[31]?/t"<<noboolalpha<<b.reset(31)<<endl;
        //reverse the bit of p
        cout<<"reverse the bit of p[31]?/t"<<noboolalpha<<b.flip(31)<<endl;
       
        //set all the bit of p
        cout<<"set all the value of b?/t"<<noboolalpha<<b.set()<<endl;
        //reset all the bit of p
        cout<<"reset all  the value of b?/t"<<noboolalpha<<b.reset()<<endl;
        //reverse all the bit of p
        cout<<"reverse all  the bit of p?/t"<<noboolalpha<<b.flip()<<endl;
       
        //covert t olong  if exceed exception
        cout<<"convert to usigned long:/t"<<noboolalpha<<b.to_ulong()<<endl;

        return 0;
}

(四)关于vector<bool>
所有关于位操作的书籍都会提及这个标准库中的另类。呵呵如果你还不知道的话,建议你好好看看相关书籍,下面是几个比较好的网址:
http://stl.winterxy.com/html/item_18.html               

如上所言,vector<bool>不是真正的stl容器,所以很多的操作跟vector并不兼容,所以使用vector要小心。
(五)还有什么容器适合于位操作呢?
 boost::dynamic_bitset
大名顶顶的boost库,不过这个还不是标准。

(写完之后,觉得有点背离初衷,呵呵,还请各位原谅)