"&"符号你知道多少

    xiaoxiao2023-03-24  3

    对于学习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.因为a1a32b1b32的取值都为0或者1,同时与运算的规则为:0&0=0,1&1=1,0&1=0,1&0=0;因此可以肯定的得出,

    a1&b1<=a1,a1&b1<=b1,因此可以x&y<=xx&y<=y

     

    定理2:对于一个无符号非0的整形数据XX对应的二进制序列a1,a2,a3…a32X-1对应的二进制序列b1,b2,b3…b32.存在一个整数p1<=p<=32,满足ap>ai,(i>p,i<=32),对于p,一定满足ap&bp=0;

     

    对于定理2的描述很复杂,用通俗的话讲,就是对于一个无符号的非零整形数据XX的二进制序列里面最右边为1的那一位,在X-1中该对应的位一定为0.

    对于定理2的证明描述起来较为复杂就不写了。当然有兴趣的同学也可以下去自己证明。不会证明的也不重要,只要记住这个结论就可以了。

     

    有了定理2,我们在返回来看上面的那段代码就比较容易弄明白了。X=x&(X-1)实际上是将X的二进制序列里面最右边的1变为了0,因此循环下去就可以计算出最开始X的二进制里面1的个数。

     

    当然对于定理2里面的一些在代码里面的特殊应用。

    比如判断一个数是不是2n次幂。经过分析,一个数是x,如果是2n次幂,那么x的二进制序列里面肯定只有一个位为1,因此x&(x-1)必定为0.这个通常可以用来判断一个数是不是2n次幂。函数代码可以精炼到下面的情况:

    bool foo(unsigned int x)

    {

             return(0==x&(x-1));

    }

     

    当然如果一个x2n次幂,那么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是不是2n次幂,如果是ret0,否则不为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是一个2n次幂的数,因此

             //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;

    }
    转载请注明原文地址: https://ju.6miu.com/read-1201395.html
    最新回复(0)