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库,不过这个还不是标准。
(写完之后,觉得有点背离初衷,呵呵,还请各位原谅)