位运算及其简单应用

先介绍一遍一些基本的运算符:&(按位与)、|(按位或)、^(按位异或)、~ (按位取反)、>> (右移运算)、<<(左移运算)。


一.逻辑运算符 

1.& 位与运算 

 1) 运算规则 

位与运算的实质是将参与运算的两个数据,按对应的二进制数逐位进行逻辑与运算。例如:int型常量47进行位与运算的运算过程如下:

4=0000 0000 0000 0100 &7 =0000 0000 0000 0111= 0000 0000 0000 0100

对于负数,按其补码进行运算。例如:例如:int型常量-47进行位与运算的运算过程如下: 4=1111 1111 1111 1100 &7 =0000 0000 0000 0111= 0000 0000 0000 0100

2) 典型应用 

(1) 清零 

清零:快速对某一段数据单元的数据清零,即将其全部的二进制位为0。例如整型数a=321对其全部数据清零的操作为a=a&0x0 321=0000 0001 0100 0001 &0=0000 0000 0000 0000

= 0000 0000 0000 0000

(2) 获取一个数据的指定位 

获取一个数据的指定位。例如获得整型数a=的低八位数据的操作为a=a&0xFF321=

0000 0001 0100 0001 & 0xFF =0000 0000 1111 11111

= 0000 0000 0100 0001

获得整型数a=的高八位数据的操作为a=a&0xFF00==a&0XFF00==

321=0000 0001 0100 0001 & 0XFF00=1111 1111 0000 0000

= 0000 0001 0000 0000

(3)保留数据区的特定位  

保留数据区的特定位。例如获得整型数a=的第7-8位(从0开始)位的数据操作为: 110000000

321=0000 0001 0100 0001 & 384=0000 0001 1000 0000

=0000 0001 0000 0000

2. | 位或运算 

1) 运算规则 

位或运算的实质是将参与运算的两个数据,按对应的二进制数逐位进行逻辑或运算。例如:int型常量57进行位或运算的表达式为5|7,结果如下:5= 0000 0000 0000 0101

| 7= 0000 0000 0000 0111=0000 0000 0000 0111

2) 主要用途 

(1) 设定一个数据的指定位。例如整型数a=321,将其低八位数据置为1的操作为a=a|0XFF321= 0000 0001 0100 0001 | 0000 0000 1111 1111=0000 0000 1111 1111

逻辑运算符||与位或运算符|的区别 

条件“或”运算符 (||) 执行 bool 操作数的逻辑“或”运算,但仅在必要时才计算第二个操作数。 x || y , x | y 不同的是,如果 x  true,则不计算 y(因为不论 y 为何值,“或”操作的结果都为 true)。这被称作为“短路”计算。

3. ^ 位异或 

 1) 运算规则 

位异或运算的实质是将参与运算的两个数据,按对应的二进制数逐位进行逻辑异或运算。只有当对应位的二进制数互斥的时候,对应位的结果才为真。例如:int型常量57进行位异或运算的表达式为5^7,结果如下:5=0000 0000 0000 0101^7=0000 0000 0000 0111

= 0000 0000 0000 0010

2) 典型应用 

 (1)定位翻转 

定位翻转:设定一个数据的指定位,将1换为00换为1。例如整型数a=321,,将其低八位数据进行翻位的操作为a=a^0XFF;

(2)数值交换 

数值交换。例如a=3,b=4。在例11-1中,无须引入第三个变量,利用位运算即可实现数据交换。以下的操作可以实现a,b两个数据的交换:

a=a^b;

b=b^a;

a=a^b;

4位非 

位非运算的实质是将参与运算的两个数据,按对应的二进制数逐位进行逻辑非运算。

 

二.位移运算符

1.位左移

左移运算的实质是将对应的数据的二进制值逐位左移若干位,并在空出的位置上填0,最高位溢出并舍弃。例如int a,b;

a=5;

b=a<<2;

则b=20,分析过程如下:

(a)10=(5)10=(0000 0000 0000 0101)2

b=a<<2;

b=(0000 0000 0001 0100)2=(20)10

从上例可以看出位运算可以实现二倍乘运算。由于位移操作的运算速度比乘法的运算速度高很多。因此在处理数据的乘法运算的时,采用位移运算可以获得较快的速度。

提示 将所有对2的乘法运算转换为位移运算,可提高程序的运行效率

2.位右移

位右移运算的实质是将对应的数据的二进制值逐位右移若干位,并舍弃出界的数字。如果当前的数为无符号数,高位补零。例如:

int (a)10=(5)10=(0000 0000 0000 0101)2

b=a>>2;

b=(0000 0000 0000 0001)2=(1)10

如果当前的数据为有符号数,在进行右移的时候,根据符号位决定左边补0还是补1。如果符号位为0,则左边补0;但是如果符号位为1,则根据不同的计算机系统,可能有不同的处理方式。可以看出位右移运算,可以实现对除数为2的整除运算。

提示 将所有对2的整除运算转换为位移运算,可提高程序的运行效率

3.复合的位运算符

在C语言中还提供复合的位运算符,如下:

&=、!=、>>=、<<=和^=

例如:a&=0x11等价于 a= a&0x11,其他运算符以此类推。

不同类型的整数数据在进行混合类型的位运算时,按右端对齐原则进行处理,按数据长度大的数据进行处理,将数据长度小的数据左端补0或1。例如char a与int b进行位运算的时候,按int 进行处理,char a转化为整型数据,并在左端补0。

补位原则如下:

1) 对于有符号数据:如果a为正整数,则左端补0,如果a 为负数,则左端补1。

2) 对于无符号数据:在左端补0。

4.例子

例11-2 获得一个无符号数据从第p位开始的n位二进制数据。假设数据右端对齐,第0位二进制数在数据的最右端,获得的结果要求右对齐。

#include <stdio.h>

/*getbits:获得从第p位开始的n位二进制数 */

unsigned int getbits(unsigned int x, unsigned int p, unsigned n)

{

unsigned int a;

unsigned int b;

a=x>>(p+1);

b=~(~0<<n);

return a&b;

}



另外一些C代码

<span class="lnum" style="color: rgb(96, 96, 96);">   1:  </span><span class="rem" style="color: rgb(0, 128, 0);">//不使用临时变量交换两个数</span>
   2:  void swap(int *a, int *b)
<span class="lnum" style="color: rgb(96, 96, 96);">   3:  </span>{
   4:      *a = *a ^ *b;
<span class="lnum" style="color: rgb(96, 96, 96);">   5:  </span>    *b = *b ^ *a;
   6:      *a = *a ^ *b;
<span class="lnum" style="color: rgb(96, 96, 96);">   7:  </span>}
   8:  //获取低8位
<span class="lnum" style="color: rgb(96, 96, 96);">   9:  </span><span class="kwrd" style="color: rgb(0, 0, 255);">short</span> getLow8(<span class="kwrd" style="color: rgb(0, 0, 255);">short</span> number)
  10:  {
<span class="lnum" style="color: rgb(96, 96, 96);">  11:  </span>    <span class="kwrd" style="color: rgb(0, 0, 255);">return</span> (number & 0XFF);
  12:  }
<span class="lnum" style="color: rgb(96, 96, 96);">  13:  </span><span class="rem" style="color: rgb(0, 128, 0);">//获取高8位</span>
  14:  short getHeight8(short number)
<span class="lnum" style="color: rgb(96, 96, 96);">  15:  </span>{
  16:      return ((number & 0XFF00) >> 8);
<span class="lnum" style="color: rgb(96, 96, 96);">  17:  </span>}
  18:  //检查一个无符号整数是否为2的幂
<span class="lnum" style="color: rgb(96, 96, 96);">  19:  </span><span class="rem" style="color: rgb(0, 128, 0);">//将一个最右侧为1的位改为0</span>
  20:  bool checkPowerOf2 (unsigned short x)
<span class="lnum" style="color: rgb(96, 96, 96);">  21:  </span>{
  22:      return !(x & (x - 1));
<span class="lnum" style="color: rgb(96, 96, 96);">  23:  </span>}
  24:  //检查一个无符号整数是否为(2的n次方 - 1)
<span class="lnum" style="color: rgb(96, 96, 96);">  25:  </span><span class="kwrd" style="color: rgb(0, 0, 255);">bool</span> checkPowerOf2Sub1(unsigned <span class="kwrd" style="color: rgb(0, 0, 255);">short</span> x)
  26:  {
<span class="lnum" style="color: rgb(96, 96, 96);">  27:  </span>    <span class="kwrd" style="color: rgb(0, 0, 255);">return</span> !(x & (x + 1));
  28:  }
<span class="lnum" style="color: rgb(96, 96, 96);">  29:  </span><span class="rem" style="color: rgb(0, 128, 0);">//析出(isolate)最右侧为1的位</span>
  30:  short isolate1(short x)
<span class="lnum" style="color: rgb(96, 96, 96);">  31:  </span>{
  32:      return x & (-x);
<span class="lnum" style="color: rgb(96, 96, 96);">  33:  </span>}
  34:  //析出(isolate)最右侧为0的位
<span class="lnum" style="color: rgb(96, 96, 96);">  35:  </span><span class="kwrd" style="color: rgb(0, 0, 255);">short</span> isolate0(<span class="kwrd" style="color: rgb(0, 0, 255);">short</span> x)
  36:  {
<span class="lnum" style="color: rgb(96, 96, 96);">  37:  </span>    <span class="kwrd" style="color: rgb(0, 0, 255);">return</span> (~x & (x + 1));
  38:  }
<span class="lnum" style="color: rgb(96, 96, 96);">  39:  </span><span class="rem" style="color: rgb(0, 128, 0);">//乘以32</span>
  40:  short mul32(short x)
<span class="lnum" style="color: rgb(96, 96, 96);">  41:  </span>{
  42:      //32 = 2 ^ 5
<span class="lnum" style="color: rgb(96, 96, 96);">  43:  </span>    <span class="kwrd" style="color: rgb(0, 0, 255);">return</span> x << 5;
  44:  }
<span class="lnum" style="color: rgb(96, 96, 96);">  45:  </span><span class="rem" style="color: rgb(0, 128, 0);">//乘以20</span>
  46:  short mul20(short x)
<span class="lnum" style="color: rgb(96, 96, 96);">  47:  </span>{
  48:      //20=2^4+2^2
<span class="lnum" style="color: rgb(96, 96, 96);">  49:  </span>    <span class="kwrd" style="color: rgb(0, 0, 255);">return</span> (x << 4) + (x << 2);
  50:  }
<span class="lnum" style="color: rgb(96, 96, 96);">  51:  </span><span class="rem" style="color: rgb(0, 128, 0);">//判断是否为奇数</span>
  52:  bool isOdd(int x)
<span class="lnum" style="color: rgb(96, 96, 96);">  53:  </span>{
  54:      return !(x & 1);
<span class="lnum" style="color: rgb(96, 96, 96);">  55:  </span>}
  56:  //比较
<span class="lnum" style="color: rgb(96, 96, 96);">  57:  </span><span class="kwrd" style="color: rgb(0, 0, 255);">int</span> min(<span class="kwrd" style="color: rgb(0, 0, 255);">int</span> x, <span class="kwrd" style="color: rgb(0, 0, 255);">int</span> y)
  58:  {
<span class="lnum" style="color: rgb(96, 96, 96);">  59:  </span>    <span class="kwrd" style="color: rgb(0, 0, 255);">return</span> ((x |~y)&((x^y) |~(y-x))) ? x : y;
  60:  }
 
             
 
             
三、
              
              
1. 变量交换
首先说个好玩的——诡异的变量交换。
交换两个int变量平时写程序会经常用到,当时,常规方法都要用三个变量,但是位运算里不需要第三个变量,仅仅需做三次异或运算:
a ^= b;
b ^= a;
a ^= b;
经过这样的三次异或运算后, a、b两个变量的值就交换过来了,原理?

原因是异或运算的逆运算就是自己,也就是一个数对一个数异或运算两次就是其本身。
2. 位运算实现int型快速乘法运算
判断奇偶性(对2取余):
a&1 == 0    偶数
a&1 == 1    奇数
计算2^n
1 << n
对2^n做乘法或除法:
a << n  等价于  a * (2 ^ n)
a >> n  等价于  a / (2 ^ n)
看到这里,也许大家会很纳闷,好好的乘法除法为什么要写成位运算呢?
其实,位运算的操作单元是bit,运算效率能提高60%。
在平时写程序的过程中,适当应用这些运算,可以大大提高程序的运行速度,下面以经典的二分求幂做一个例子:
int Power(int a, int n, int mod) // cal (a^n)%mod
{
int ans = 1;
while (n > 0) {
if (n & 1) {
ans *= a;
n--;
} else {
a *= a;
n >>= 1;
}
ans %= mod;
}
return ans;
}
3. 位运算的其他应用
(1) 取int型变量a的第k位 (k=0,1,2……sizeof(int))
a>>k&1
(2) 将int型变量a的第k位清0
a=a&~(1<<k)
(3) 将int型变量a的第k位置1
a=a|(1<<k)
(4) int型变量循环左移k次
a=a<<k|a>>16-k   (设sizeof(int)=16)
(5) int型变量a循环右移k次
a=a>>k|a<<16-k   (设sizeof(int)=16)
(6) 实现最低n位为1,其余位为0的位串信息:
~(~0 << n)
(7)截取变量x自p位开始的右边n位的信息:
(x >> (1+p-n)) & ~(~0 << n)
(8)截取old变量第row位,并将该位信息装配到变量new的第15-k位
new |= ((old >> row) & 1) << (15 – k)
(9)设s不等于全0,代码寻找最右边为1的位的序号j:
for(j = 0; ((1 << j) & s) == 0; j++) ;



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值