C语言的位运算有一下六中: & 按位与 | 按位或 ^ 按位亦或 ~ 按位取反 << 左移 >> 右移
按位与& 两个对应的位为1,运算后对应位为1,否则为0,比如:10101100 & 01101001 = 00101000。
按位或| 两个对应位中只要有一个为1,运算后对应位就为1,否则为0,比如:10101100 | 01101001 = 11101101。
按位亦或^ 两个对应位中如果不同,运算后对应位就为1,否则相同就为0,比如:10101100 ^ 01101001 = 11000101。
按位取反~ 将原来对应位取反,1变0,0变1。这个是一个单目运算,也就是只需要一个操作数,比如:~10101100 = 01010011。
左移<< 将整个位左移指定位数,比如:10101100 << 3,结果为01100 000。前面的三位101被移走,后面补充3个0。
右移>> 将整个位右移指定位数,比如:10101100 >> 3,结果为多少?右移有点不一样,它分逻辑右移和算术右移。如果是逻辑右移,向右移动后,左边补0;如果是算术右移,则左边最高位是0时,就补0,如果左边最高位是1,则根据编译器不同可能会不同。比如:逻辑右移:10101100 >> 3,结果为000 10101。右边的100被移走,左边补0;算术右移:10101100 >> 3,结果可能为111 10101,也可能为000 10101。当然右边的100还是被移走。
以上基本的位运算其实很简单,很多人一看就理解了,但是要灵活应用却不是那么简单的。比如有群友提出清除位是怎么回事,其实就是这个为位运算了,将一个数的某一个位设置为0。比如我们要将10101100这个二进制位的左边第三位清除,则可以用如下办法:10101100 & 11011111。
一般实际编程中都用一个mask来清除和设置。像上面这个问题,我们定义一个mask为0x20的数(00100000),要设置左边第三位,者只需要10101100 | mask;要清除时,用10101100 & ~mask即可。这样所有位我们都可以定义mask来操作。这个在嵌入式上用的非常广泛,比如设置和清除寄存器。类似的应用还有很多,下面举一个例子:
位映射:
unsigned char data[32]; #define BIT_SET(a, n) (((a)[(n) >> 3]) |= (1 << ((n) & 0x07))) #define BIT_GET(a, n) ((((a)[(n) >> 3]) >> ((n) & 0x07)) & 0x01) #define BIT_CLR(a, n) (((a)[(n) >> 3]) &= (~(1 << ((n) & 0x07))))
它可以用一个位来记录某个对应的操作。
求绝对值:
int myabs(int n) { int f = n >> 31; n ^= f; n -= f; return n; }
计算一个整数中有多少位是1:
int count(unsigned int v) { v -= (v>>1) & 0x55555555; v = (v & 0x33333333) + ((v>>2) & 0x33333333); v = (v + (v>>4)) & 0x0F0F0F0F; v += v>>8; v += v>>16; return v & 0x3F; }
颠倒一个字节的位序:
unsigned char reverse(unsigned char b) { b = (b << 4) + (b >> 4); b = ((b & 0x33) << 2) + ((b & 0xCC) >> 2); b = ((b & 0x55) << 1) + ((b & 0xAA) >> 1); return b; }
奇偶校验计算:
static int Parity(int val) { int parity = val; parity ^= parity >> 16; parity ^= parity >> 8; parity ^= parity >> 4; parity ^= parity >> 2; parity ^= parity >> 1; return parity & 0x00000001; }
还有更复杂的汉明码解码等就不在这里举例了,位运算本身是很简单的,但是真正应用起来是非常复杂的。但是他的效率也很高,同样一个任务,往往用位运算能提高速度。