对于学习c/c++的人来说,都应该知道这个符号的,如果你连这个运算符都不知道,可以说你压根就没有学过c/c++,虽然这个符号大家都知道是按位与运算符,但是很多时候看到别人的代码里面有这个运算的时候,就感觉别人的代码好难懂,好难理解。不信就来几段实际的代码看看。
int count(unsigned char n)
{
intsum=0;
while(n)
{
n&=(n-1);
sum++;
}
returnsum;
}
对于稍微有点经验的人都知道这段代码是干什么用的,但是对于很多人我相信还是不知道这段代码是干什么用的,当然还有一部分人知道这段代码是干什么用的,但是说不出来为什么这段代码就能够实现那个功能。接下来我就简单的说一下这段代码。高手不要笑话,如果有错误的地方,还请多多指正。
首先我第一次看到这段代码的时候,是在一套笔试题里面,然后我当时不知道这段代码的作用,后来到网上查了一下,知道这是微软的一套笔试题,这个函数是用来统计n的二进制表示里面1的个数的。当时怎么也想不明白,为什么这样就可以计算出n的二进制里面1的个数。首先给出两个位运算满足的定理!
定理1:对于任意的同类型的两个整形数据x,y,必定满足x&y<=x,当然也满足x&y<=y。
证明:对于任意的X,肯定可以表示为X=a1*2^31+a2*2^30+…+a32*2^0.其中a1,a2…,a32的取值为0或者1,同理Y=b1*2^31+b2*2^30+…+b32*2^0.
因此X&Y=(a1&b1)*10^31+(a2&b2)*10^30+…+(a32&b32)*10^0.因为a1至a32和b1至b32的取值都为0或者1,同时与运算的规则为:0&0=0,1&1=1,0&1=0,1&0=0;因此可以肯定的得出,
a1&b1<=a1,a1&b1<=b1,因此可以x&y<=x,x&y<=y。
定理2:对于一个无符号非0的整形数据X,X对应的二进制序列a1,a2,a3…a32,X-1对应的二进制序列b1,b2,b3…b32.存在一个整数p,1<=p<=32,满足ap>ai,(i>p,i<=32),对于p,一定满足ap&bp=0;
对于定理2的描述很复杂,用通俗的话讲,就是对于一个无符号的非零整形数据X,X的二进制序列里面最右边为1的那一位,在X-1中该对应的位一定为0.
对于定理2的证明描述起来较为复杂就不写了。当然有兴趣的同学也可以下去自己证明。不会证明的也不重要,只要记住这个结论就可以了。
有了定理2,我们在返回来看上面的那段代码就比较容易弄明白了。X=x&(X-1)实际上是将X的二进制序列里面最右边的1变为了0,因此循环下去就可以计算出最开始X的二进制里面1的个数。
当然对于定理2里面的一些在代码里面的特殊应用。
比如判断一个数是不是2的n次幂。经过分析,一个数是x,如果是2的n次幂,那么x的二进制序列里面肯定只有一个位为1,因此x&(x-1)必定为0.这个通常可以用来判断一个数是不是2的n次幂。函数代码可以精炼到下面的情况:
bool foo(unsigned int x)
{
return(0==x&(x-1));
}
当然如果一个x是2的n次幂,那么x的二进制序列里面只有一位为1,假定第p位为1,那么x-1的二进制序列一定满足从第p+1位到末尾全都为1,这个自己想一想就能够明白。因此对于任意的一个整型数y,y&(x-1)的值和y%x的值相同。只有明白了这个道理才能看明白下面的代码。
这段代码是通过环形缓冲区实现的队列,代码很精炼。当然不是我写的,我是在网上看到的,拿到这段代码的时候,没有任何注释,然后自己看明白了,添上了一部分注释。也是在看这段代码的时候,总结出了上面的一些东西。其实我个人猜想stl里面的队列应该也是用的这个原理,当然他的功能更强大,但是基本思想肯定和这个一样的。
环形缓冲区实现的队列代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#define BUFFSIZE 1024 * 1024
#define min(x, y) ((x) < (y) ? (x) : (y))
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
//环形缓冲区结构体定义
struct cycle_buffer {
unsigned char *buf;
unsigned int size;
unsigned int in;
unsigned int out;
pthread_mutex_t lock;
};
//环形缓冲区全局的指针
static struct cycle_buffer *fifo = NULL;
//初始化环形缓冲区
static int init_cycle_buffer(void)
{
int size = BUFFSIZE,ret;
ret = size & (size -1); //判断size是不是2的n次幂,如果是ret为0,否则不为0
if (ret)
return ret;
fifo = (structcycle_buffer *) malloc(sizeof(struct cycle_buffer)); //分配缓冲区对象
if (!fifo)
return -1;
memset(fifo, 0,sizeof(struct cycle_buffer)); //填充为0
fifo->size = size; //初始化缓冲区大小
fifo->in = fifo->out= 0; //初始化读写下标
pthread_mutex_init(&fifo->lock, NULL); //初始化锁
fifo->buf = (unsignedchar *) malloc(size); //分配缓冲区大小
if (!fifo->buf)
free(fifo);
else
memset(fifo->buf,0, size); //缓冲区设置为0
return 0;
}
//读缓冲区
unsigned int fifo_get(unsigned char *buf, unsigned int len)
{
unsigned int l;
//求得此次能实际够读取数据的长度
len = min(len, fifo->in- fifo->out);
//由于fifo->size是一个2的n次幂的数,因此
//fifo->out&(fifo->size-1)和fifo->out%fifo->size的值相同
//那么fifo->size-(fifo->out& (fifo->size-1))就是读数据下
//标到缓冲区末尾的长度,后面的就容易理解了
l = min(len, fifo->size- (fifo->out & (fifo->size - 1)));
memcpy(buf,fifo->buf + (fifo->out & (fifo->size - 1)), l);
memcpy(buf + l,fifo->buf, len - l);
fifo->out += len;
//返回实际读取的数据的长度
return len;
}
//写缓冲区
unsigned int fifo_put(unsigned char *buf, unsigned int len)
{
unsigned int l;
len = min(len,fifo->size - fifo->in + fifo->out);
l = min(len, fifo->size- (fifo->in & (fifo->size - 1)));
memcpy(fifo->buf +(fifo->in & (fifo->size - 1)), buf, l);
memcpy(fifo->buf, buf +l, len - l);
fifo->in += len;
return len;
}
static void * thread_read(void *arg)
{
char buf[1024];
unsigned int n;
pthread_detach(pthread_self()); //线程分离
for (;;) {
memset(buf, 0,sizeof(buf));
pthread_mutex_lock(&fifo->lock);
n = fifo_get(buf,sizeof(buf));
pthread_mutex_unlock(&fifo->lock);
write(STDOUT_FILENO,buf, n);
}
printf("nnafterthread_read : %snn",buf);
return NULL;
}
static void * thread_write(void *arg)
{
unsigned char buf[] ="hello world";
pthread_detach(pthread_self());
for (;;) {
pthread_mutex_lock(&fifo->lock);
fifo_put(buf,strlen(buf));
pthread_mutex_unlock(&fifo->lock);
}
return NULL;
}
int main(void)
{
int ret;
pthread_t wtid, rtid;
ret =init_cycle_buffer();
if (ret == -1)
return ret;
pthread_create(&wtid,NULL, thread_write, NULL);
pthread_create(&rtid,NULL, thread_read, NULL);
pthread_exit(NULL); //保证主线程退出了,子线程还是能够运行
return 0;
}