C++位操作包括两种:传统的C语言方式的位操作和C++中利用bitset容器的位操作
一、传统的C方式位操作:
1.基本操作:

   使用一个unsigned int变量来作为位容器。
2.操作符:
|   按位或操作符:result=exp1|exp2;当exp1和exp2中对应位中至少有一个为1时,result中对应位为1,否则为0。
&  按位与操作符::result=exp1&exp2;当exp1和exp2中对应位全为1时,result中对应位为1,否则为0。
^  按位异或或操作符:result=exp1^exp2;当exp1和exp2中对应位不相同时,result中对应位为1,否则为0。
~  反转操作符:将位容器中的所有位都反转,1变为0,0变为1。
<< 按位左移操作符:exp<<n,将容器中所有的位向左移n位,空出的位用0填充。
>> 按位右移操作符:exp>>n,将容器中所有的位向右移n位,空出的位用0填充。
|=,&=,^= 分别对应|&^三种操作符的复合操作符。
3.常用操作
这里我们假设有一个result的unsigned int变量用来储存32个学生的成绩(通过和不通过分别用0和1),这样result就有33位(result从右至左,从0开始计算位数,在这个例子中0位被浪费)。
(a) 将第27位设置为及格(设作1)其他位不变:
   result|=(1<<27) //任意的位值与1作按位或操作其值为1,而与0作按位与操作其值不变
(b) 将第27位设置成不及格(设为0)。
   result&=~(1<<27) //任意的位值与0作按位与操作其值为0,而与1作按位与操作其值不变
(c) 反转第27位的值。
   result^=(1<<27) //任意的位值与1作按位异或操作其值为1,而与0作按位异与操作其值不变

二、C++中的bitset容器
1.头文件:

  #include <bitset>
2.声明一个容器:
(a)声明一个指定位数的空容器(所有位设为0): bitset<int> bits;
(b)声明一个指定位数并将指定的几个位初始化为相应值的容器: bitset<n> bits(int);
     bitdet<int> bits(string&)
总结:bitset模板类中类型参数传递容器的位数,而构造函数参数通过一个int或一个string&值来从右至左初始化容器中的相应值。
3.bitset的基本用法:
操作                            功能                                   用法
test(pos)                       pos位是否为1                    a.test(4)
any()                            任意位是否为1                   a.any()
none()                          是否没有位为1                   a.none()
count()                         值是1的位的小数              a.count()
size()                           位元素的个数                     a.size()
[pos]                            访问pos位                         a[4]
flip()                             翻转所有位                         a.flip()
flip(pos)                        翻转pos位                         a.flip(4)
set()                            将所有位置1                      a.set()
set(pos)                       将pos位置1                       a.set(4)
reset()                         将所有位置0                      a.reset()
reset(pos)                    将pos位置0                       a.reset(4)
4.bitset与传统C位操作及字符串的转换
   可以通过to_string()成员将容器转输出为一个string字符串,另外还可以用to_long()成员将容器输出到传统的用于C风格的位容器中。如:
  unsigned long bits = bits.to_long();
  sting str(bits.to_string());

 

C语言中常见的置位操作 如何对某一位置0或者置1?
方法一:
写成宏,方便移植
#define setbit(x,y) x|=(1<<y) //将X的第Y位置1
#define clrbit(x,y) x&=!(1<<y) //将X的第Y位清0


方法二:
C语言位运算除了可以提高运算效率外,在嵌入式系统的编程中,它的另一个最典型的应用,而且十分广泛地正在被使用着的是位间的与(&)、或(|)、非(~)操作,这跟嵌入式系统的编程特点有很大关系。我们通常要对硬件寄存器进行位设置

譬如,我们通过将AM186ER型80186处理器的中断屏蔽控制寄存器的
第低6位设置为0(开中断2),最通用的做法是:
#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp &~INT_I2_MASK);

而将该位设置为1的做法是:
#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
outword(INT_MASK, wTemp | INT_I2_MASK);

判断该位是否为1的做法是:
#define INT_I2_MASK 0x0040
wTemp = inword(INT_MASK);
if(wTemp & INT_I2_MASK)
{
… /* 该位为1 */
}

方法三:
int a|=(1<<x) //X就是某位需要置1的数字,如第四位置1为: a|=(1<<4)
int b&=~(1<<x) //把某位置0
x=x|0x0100    //把第三位置1
x=x&0x1011    //把第三位置0
#define BitGet(Number,pos) ((Number) >> (pos)&1)) //用宏得到某数的某位
#define BitGet(Number,pos) ((Number) |= 1<<(pos)) //把某位置1
#define BitGet(Number,pos) ((Number) &= ~(1<<(pos)) //把某位置0
#define BitGet(Number,pos) ((Number) ^= 1<<(pos)) //把Number的POS位取反
典型操作有:
WTCON |=  (1 << 5) //WTCON的第五位清1
WTCON &= ~(1 << 5) //WTCON的第五位清0  

上述方法在嵌入式系统的编程中是非常常见的,我们需要牢固掌握

 

 

C/C++位操作技巧  检测一个无符号数是不为2^n-1(^为幂):   x&(x+1)   
   
  将最右侧0位改为1位:   x   |   (x+1)   
   
  二进制补码运算公式:   
  -x   =   ~x   +   1   =   ~(x-1)   
  ~x   =   -x-1     
  -(~x)   =   x+1   
  ~(-x)   =   x-1   
  x+y   =   x   -   ~y   -   1   =   (x|y)+(x&y)     
  x-y   =   x   +   ~y   +   1   =   (x|~y)-(~x&y)     
  x^y   =   (x|y)-(x&y)   
  x|y   =   (x&~y)+y   
  x&y   =   (~x|y)-~x   
   
  x==y:         ~(x-y|y-x)   
  x!=y:         x-y|y-x   
  x<   y:         (x-y)^((x^y)&((x-y)^x))   
  x<=y:         (x|~y)&((x^y)|~(y-x))   
  x<   y:         (~x&y)|((~x|y)&(x-y))//无符号x,y比较   
  x<=y:         (~x|y)&((x^y)|~(y-x))//无符号x,y比较   
   
   
  使用位运算的无分支代码:   
   
  计算绝对值   
  int   abs(   int   x   )     
  {   
  int   y   ;   
  y   =   x   >>   31   ;   
  return   (x^y)-y   ;//or:   (x+y)^y   
  }   
   
  符号函数:sign(x)   =   -1,   x<0;   0,   x   ==   0   ;   1,   x   >   0   
  int   sign(int   x)   
  {   
  return   (x>>31)   |   (unsigned(-x))>>31   ;//x=-2^31时失败(^为幂)   
  }   
   
  三值比较:cmp(x,y)   =   -1,   x<y;   0,   x==y;   1,   x   >   y   
  int   cmp(   int   x,   int   y   )   
  {   
  return   (x>y)-(x-y)   ;   
  }   
   
  doz=x-y,   x>=y;   0,   x<y   
  int   doz(int   x,   int   y   )   
  {   
  int   d   ;   
  d   =   x-y   ;   
  return   d   &   ((~(d^((x^y)&(d^x))))>>31)   ;   
  }   
   
  int   max(int   x,   int   y   )     
  {   
  int   m   ;   
  m   =   (x-y)>>31   ;     
  return   y   &   m   |   x   &   ~m   ;   
  }   
   
  不使用第三方交换x,y:   
  1.x   ^=   y   ;   y   ^=   x   ;   x   ^=   y   ;   
  2.x   =   x+y   ;   y   =   x-y   ;   x   =   x-y   ;   
  3.x   =   x-y   ;   y   =   y+x   ;   x   =   y-x   ;   
  4.x   =   y-x   ;   x   =   y-x   ;   x   =   x+y   ;     
   
  双值交换:x   =   a,   x==b;   b,   x==a//常规编码为x   =   x==a   ?   b   :a   ;   
  1.x   =   a+b-x   ;   
  2.x   =   a^b^x   ;   
   
  下舍入到2的k次方的倍数:   
  1.x   &   ((-1)<<k)   
  2.(((unsigned)x)>>k)<<k   
  上舍入:   
  1.   t   =   (1<<k)-1   ;   x   =   (x+t)&~t   ;   
  2.t   =   (-1)<<k   ;   x   =   (x-t-1)&t   ;   
   
  位计数,统计1位的数量:   
  1.   
  int   pop(unsigned   x)   
  {   
  x   =   x-((x>>1)&0x55555555)   ;   
  x   =   (x&0x33333333)   +   ((x>>2)   &   0x33333333   )   ;   
  x   =   (x+(x>>4))   &   0x0f0f0f0f   ;   
  x   =   x   +   (x>>8)   ;   
  x   =   x   +   (x>>16)   ;   
  return   x   &   0x0000003f   ;   
  }   
  2.   
  int   pop(unsigned   x)   {   
  static   char   table[256]   =   {   0,1,1,2,   1,2,2,3,   ....,   6,7,7,8   }   ;   
  return   table[x&0xff]+table[(x>>8)&0xff]+table[(x>>16)&0xff]+table[(x>>24)]   ;   
  }   
   
  奇偶性计算:   
  x   =   x   ^   (   x>>1   )   ;   
  x   =   x   ^   (   x>>2   )   ;   
  x   =   x   ^   (   x>>4   )   ;   
  x   =   x   ^   (   x>>8   )   ;   
  x   =   x   ^   (   x>>16   )   ;   
  结果中位于x最低位,对无符号x,结果的第i位是原数第i位到最左侧位的奇偶性   
   
   
  位反转:   
  unsigned   rev(unsigned   x)   
  {   
  x   =   (x   &   0x55555555)   <<   1   |   (x>>1)   &   0x55555555   ;   
  x   =   (x   &   0x33333333)   <<   2   |   (x>>2)   &   0x33333333   ;   
  x   =   (x   &   0x0f0f0f0f)   <<   4   |   (x>>4)   &   0x0f0f0f0f   ;   
  x   =   (x<<24)   |   ((x&0xff00)<<8)   |   ((x>>8)   &   0xff00)   |   (x>>24)   ;   
  return   x   ;   
  }   
   
  递增位反转后的数:   
  unsigned   inc_r(unsigned   x)   
  {   
  unsigned   m   =   0x80000000   ;   
  x   ^=   m   ;   
  if(   (int)x   >=   0   )     
  do   {   m   >>=   1   ;   x   ^=   m   ;   }   while(   x   <   m   )   ;   
  return   x   ;   
  }   
   
  混选位:   
  abcd   efgh   ijkl   mnop   ABCD   EFGH   IJKL   MNOP->aAbB   cCdD   eEfF   gGhH   iIjJ   kKlL   mMnN   oOpP   
  unsigned   ps(unsigned   x)   
  {   
  unsigned   t   ;   
  t   =   (x   ^   (x>>8))   &   0x0000ff00;   x   =   x   ^   t   ^   (t<<8)   ;   
  t   =   (x   ^   (x>>4))   &   0x00f000f0;   x   =   x   ^   t   ^   (t<<4)   ;   
  t   =   (x   ^   (x>>2))   &   0x0c0c0c0c;   x   =   x   ^   t   ^   (t<<2)   ;   
  t   =   (x   ^   (x>>1))   &   0x22222222;   x   =   x   ^   t   ^   (t<<1)   ;   
  return   x   ;   
  }   
   
  位压缩:   
  选择并右移字x中对应于掩码m的1位的位,如:compress(abcdefgh,01010101)=0000bdfh   
  compress_left(x,m)操作与此类似,但结果位在左边:   bdfh0000.   
  unsigned   compress(unsigned   x,   unsigned   m)   
  {   
  unsigned   mk,   mp,   mv,   t   ;   
  int   i   ;   
   
  x   &=   m   ;   
  mk   =   ~m   <<   1   ;   
  for(   i   =   0   ;   i   <   5   ;   ++i   )   {   
  mp   =   mk   ^   (   mk   <<   1)   ;   
  mp   ^=   (   mp   <<   2   )   ;   
  mp   ^=   (   mp   <<   4   )   ;   
  mp   ^=   (   mp   <<   8   )   ;   
  mp   ^=   (   mp   <<   16   )   ;   
  mv   =   mp   &   m   ;   
  m   =   m   ^   mv   |   (mv   >>   (1<<i)   )   ;   
  t   =   x   &   mv   ;   
  x     =   x   ^   t   |   (   t   >>   (   1<<i)   )   ;   
  mk   =   mk   &   ~mp   ;   
  }   
  return   x   ;   
  }   
   
   
  位置换:   
  用32个5位数表示从最低位开始的位的目标位置,结果是一个32*5的位矩阵,   
  将该矩阵沿次对角线转置后用5个32位字p[5]存放。   
  SAG(x,m)   =   compress_left(x,m)   |   compress(x,~m)   ;   
  准备工作:   
  void   init(   unsigned   *p   )   {   
  p[1]   =   SAG(   p[1],   p[0]   )   ;   
  p[2]   =   SAG(   SAG(   p[2],   p[0]),   p[1]   )   ;   
  p[3]   =   SAG(   SAG(   SAG(   p[3],   p[0]   ),   p[1]),   p[2]   )   ;   
  p[4]   =   SAG(   SAG(   SAG(   SAG(   p[4],   p[0]   ),   p[1])   ,p[2]),   p[3]   )   ;   
  }   
  实际置换:   
  int   rep(   unsigned   x   )   {   
  x   =   SAG(x,p[0]);   
  x   =   SAG(x,p[1]);   
  x   =   SAG(x,p[2]);   
  x   =   SAG(x,p[3]);   
  x   =   SAG(x,p[4]);   
  return   x   ;   
  }   
   
  二进制码到GRAY码的转换:   
  unsigned   B2G(unsigned   B   )   
  {   
  return   B   ^   (B>>1)   ;   
  }   
  GRAY码到二进制码:   
  unsigned   G2B(unsigned   G)   
  {   
  unsigned   B   ;   
  B   =   G   ^   (G>>1)   ;   
  B   =   G   ^   (G>>2)   ;   
  B   =   G   ^   (G>>4)   ;   
  B   =   G   ^   (G>>8)   ;   
  B   =   G   ^   (G>>16)   ;   
  return   B   ;   
  }   
   
  找出最左0字节的位置:   
  int   zbytel(   unsigned   x   )   
  {   
  static   cahr   table[16]   =   {   4,3,2,2,   1,1,1,1,   0,0,0,0,   0,0,0,0   }   ;   
  unsigned   y   ;   
  y   =   (x&0x7f7f7f7f)   +   0x7f7f7f7f   ;   
  y   =   ~(y|x|0x7f7f7f7f)   ;   
  return   table[y*0x00204081   >>   28]   ;//乘法可用移位和加完成   
  }

 

在第一节概述里就说了,C语言是一种中级语言,能对计算机硬件直接操作,这就涉及到位的概念。

一、位的概念
    我们知道,在计算机中,一字节占8位(现在的某些电脑也有占16位的),这样表示的数的范围为0-255,也即00000000-11111111。位就是里面的0和1。
        char c=100;
    实际上c应该是01100100,正好是64H。其中高位在前,低位在后。                       |             |
                           第7位   第0位

二、位逻辑运算符

        符号         描述
         &           位逻辑与
         |           位逻辑或
         ^           位逻辑异或
         ~           取补

    表中除去最后一个运算符是单目运算符,其他都是双目运算符。这些运算符只能用于整型表达式。位逻辑运算符通常用于对整型变量进行位的设置、清零、取反、以及对某些选定的位进行检测。在程序中一般被程序员用来作为开关标志。较低层次的硬件设备驱动程序,经常需要对输入输出设备进行位操作。

         & 运算的规则是当两个位都为1时,结果为1,否则为0;
         | 运算的规则是当两个位都为0时,结果为0,否则为1;
         ^ 运算的规则是当两个位相同时,结果为0,否则为1;
         ~ 运算的规则是当为1时结果为0,当为0时,结果为1。

    设置位:设置某位为1,而其他位保持不变,可以使用位逻辑或运算。
        char c;
        c=c|0x40;
    这样不论c原先是多少,和01000000或以后,总能使第6位为1,而其他位不变。

    清除位:设置某位为0,而其他位保持不变。可以使用位逻辑与运算。
        c=c&0xBF;
    这样c和10111111与以后,总能使第6位为0,其他位保持不变。
    那如果想让某位为1,其他位都为0怎么办呢?

三、位移运算符
         符号             描述
          <<              左移
          >>              右移

    位移运算符作用于其左侧的变量,其右侧的表达式的值就是移动的位数,运算结果就是移动后的变量结果。
        b=a<<2;
    就是a的值左移两位并赋值为b。a本身的值并没有改变。
               
    向左移位就是在低位沙锅补0,向右移位就是在高位上补0。右移时可以保持结果的符号位,也就是右移时,如果最高位为1,是符号位,则补1而不是补0。
    程序员常常对右移运算符来实现整数除法运算,对左移运算符来实现整数乘法运算。其中用来实现乘法和除法的因子必须是2的幂次。

    举例:输入一个整数,判断这个数中有几个二进制位1?例如输入67,输出结果应该为3。因为67的相应二进制数为00000000 01000011(0043H),有3个1出现。
    分析:要判断是不是1,只需要判断该位与1与以后是不是1就可以知道。一个整数,判断16次即可。

        main()
        {
            int num,k;
            int count=0;               /* 记录1的个数 */
            scanf(\"%d\",&num);
            for(k=0;k<16;k++)
            {
                if(num&1==1) count++;    /* 判断最低位是不是1 */
                num>>=1;                 /* num右移1位 */

               }
             printf(\"%d\\n\",count);
        }

    这样每次都判断最低位是不是1,判断完以后,让前面的右移一位即可。
    对位的操作,一般程序中用的不多,但是在对计算机硬件操作时,肯定会涉及到。例如,我们以后要讲到的对串口和声卡操作就要用到一些。



单片机的C语言中位操作用法

在对单处机进行编程的过程中,对位的操作是经常遇到的。C51对位的操控能力是非常强大的。从这一点上,就可以看出C不光具有高级语言的灵活性,又有低级语言贴近硬件的特点。这也是在各个领域中都可以看到C的重要原因。在这一节中将详细讲解C51中的位操作及其应用。

1
、位运算符

C51
提供了几种位操作符,如下表所示:
运算符含义运算符含义
&按位与~取反
|按位或<< 左移
^按位异或>> 右移

1
)“按位与”运算符(&
参加运算的两个数据,按二进位进行“与”运算。原则是全11,00,即:


0&0=0; 0&1=0; 1&0=0; 1&1=1;

如下例:

a=5&3; //a=(0b 0101) & (0b 0011) =0b 0001 =1

那么如果参加运算的两个数为负数,又该如何算呢?会以其补码形式表示的二进制数来进行与运算。

a=-5&-3; //a=(0b 1011) & (0b1101) =0b 1001 =-7

在实际的应用中与操作经常被用于实现特定的功能:

1.
清零
“按位与”通常被用来使变量中的某一位清零。如下例:

a=0xfe;
//a=0b 11111110


a=a&0x55;

//使变量a的第1位、第3位、第5位、第7位清零 a= 0b
01010100


2.
检测位


要知道一个变量中某一位是‘1还是‘0,可以使用与操作来实现。

a=0xf5;
//a=0b 11110101


result=a&0x08; //
检测a的第三位,result=0


3.
保留变量的某一位
要屏蔽某一个变量的其它位,而保留某些位,也可以使用与操作来实现。
a=0x55; //a=0b 01010101
a=a&0x0f; //将高四位清零,而保留低四位
a=0x05

 2)“按位或”运算符(|)            
      参与或操作的两个位,只要有一个为‘1’,则结果为‘1’。即有‘1’为‘1’,全‘0’为‘0’。
             
0|0=0; 0|1=1; 1|0=1; 1|1=1;

例如:
        a=0x30|0x0f; //a=(0b00110000)|(0b00001111)=(0b00111111)=0x3f
“按位或”运算最普遍的应用就是对一个变量的某些位置‘1。如下例:
a=0x00; //a=0b 00000000
a=a|0x7f; //a的低7位置为1,a=0x7f


3)
“异或”运算符(^)
      异或运算符^又被称为XOR运算符。当参与运算的两个位相同(‘1’与‘1’或‘0’与‘0’)时结果为‘0’。不同时为‘1’。即相同为0,不同为1
     
0^0=0; 0^1=1; 1^0=1;1^1=0;

例如:
        a=0x55^0x3f; //a=(0b01010101)^(0b00111111)=(0b01101010)=0x6a
异或运算主要有以下几种应用:
      1.翻转某一位
         当一个位与‘1’作异或运算时结果就为此位翻转后的值。如下例:
a=0x35; //a=0b00110101
a=a^0x0f; //a=0b00111010
a
的低四位翻转

         关于异或的这一作用,有一个典型的应用,即取浮点的相反数,具体的实现如下:
f=1.23; //f为浮点型变量 值为1.23
f=f*-1; //f乘以-1,实现取其相反数,要进行一次乘运算

f=1.23;
((unsigned char *)&f)[0]^=0x80;
//
将浮点数f的符号位进行翻转实现取相反数
       
      2.保留原值
       当一个位与‘0’作异或运算时,结果就为此位的值。如下例:
a=0xff; //a=0b11111111
a=a^0x0f; //a=0b11110000 0x0f作异或,高四位不变,低四位翻转

      3.交换两个变量的值,而不用临时变量
       要交换两个变量的值,传统的方法都需要一个临时变量。实现如下:
void swap(unsigned char *pa,unsigned char *pb)
{

unsigned char temp=*pa;//
定义临时变量,将pa指向的变量值赋给它

*pa=*pb;


*pb=temp;
 //变量值对调
}

而使用异或的方法来实现,就可以不用临时变量,如下:
void swap_xor(unsigned char *pa,unsigned char *pb)
{

*pa=*pa^*pb;


*pb=*pa^*pb;


*pa=*pa^*pb;
//
采用异或实现变量对调
}

从上例中可以看到异或运算在开发中是非常实用和神奇的。
 4)“取反”运算符(~
         与其它运算符不同,“取反”运算符为单目运算符,即它的操作数只有一个。它的功能就是对操作数按位取反。也就是是‘1’得‘0’,是‘0’得‘1’。
            
~1=0; ~0=1;




如下例:
a=0xff; //a=0b11111111
a=~a; //a=0b00000000


1.
对小于0的有符号整型变量取相反数

d=-1;

//d为有符号整型变量,赋值为-1,内存表示为0b 11111111 11111111

d=~d+1; //
d的相反数,d=1,内存表示0b 00000000 00000001
        
  此例运用了负整型数在内存以补码方式来存储的这一原理来实现的。负数的补码方式是这样的:负数的绝对值的内存表示取反加1,就为此负数的内存表示。如-23如果为八位有符号整型数,则其绝对值23的内存表示为0b00010111,对其取反则为0b11101000,再加10b11101001,即为0XE9,与Keil仿真结果是相吻合的:

   2.增强可移植性
          关于“增强可移植性”用以下实例来讲解:
          假如在一种单片机中unsigned char类型是八个位(1个字节),那么一个此类型的变量a=0x67,对其最低位清零。则可以用以下方法:
a=0x67; //a=0b 0110 0111

a=a&0xfe; //a=0b 0110 0110

上面的程序似乎没有什么问题,使用0xfe这一因子就可以实现一个unsigned char型的变量最低位清零。但如果在另一种单片机中的unsigned char类型被定义为16个位(两个字节),那么这种方法就会出错,如下:
b=0x6767; //假设b为另一种单片机中的unsigned char 类型变量,值为0b 0110 0111 0110 0111

b=b&0xfe; //
如果此时因子仍为0xfe的话,则结果就为0b 0000 0000 0110 0110 0x0066,而与0x6766不相吻合

上例中的问题就是因为不同环境中的数据类型差异所造成的,即程序的可移植性不好。对于这种情况可以采用如下方法来解决:
a=0x67; //a=0b 0110 0111
a=a&~1; //在不同的环境中~1将自动匹配运算因子,实现最后一位清零 a=0x66
其中~1 0b 11111110
b=0x6767; //a=0b 0110 0111 0110 0111
b=a&~1; //~1=0b 1111 1111 1111 1110b=0b 0110 0111 0110 0110 ,即0x6766


5
)左移运算符(<<
  左移运算符用来将一个数的各位全部向左移若干位。如:
         
a=a<<2

表示将a的各位左移2位,右边补0。如果a=34(0x220b00100010),左移2位得0b10001000,即十进制的136。高位在左移后溢出,不起作用。
          从上例可以看到,a被左移2位后,由34变为了136,是原来的4倍。而如果左移1位,就为0b01000100,即十进制的68,是原来的2倍,很显然,左移N位,就等于乘以了2N。但一结论只适用于左移时被溢出的高位中不包含‘1’的情况。比如:
a=64; //a=0b 0100 0000
a=a<<2; //a=0b 0000 0000





其实可以这样来想,aunsigned char型变量,值为64,左移2位后等于乘以了4,即64X4256,而此种类型的变量在表达256时,就成为了0x00,产生了一个进位,即溢出了一个‘1
          在作乘以2N这种操作时,如果使用左移,将比用乘法快得多。因此在程序中适应的使用左移,可以提高程序的运行效率。

6
)右移运算符
         右移与左移相类似,只是位移的方向不同。如:
                 
a=a>>1



表示将a的各位向右移动1位。与左移相对应的,左移一位就相当于除以2,右移N位,就相当于除以2N
          在右移的过程中,要注意的一个地方就是符号位问题。对于无符号数右移时左边高位移和‘0’。对于有符号数来说,如果原来符号位为‘0’,则左边高位为移入‘0’,而如果符号位为‘1’,则左边移入‘0’还是‘1’就要看实际的编译器了,移入‘0’的称为“逻辑右移”,移入‘1’的称为“算术右移”。Keil中采用“算术右移”的方式来进行编译。如下:
d=-32; //d为有符号整型变量,值为-32,内存表示为0b 11100000
d=d>>1;//右移一位 d 0b 11110000 -16Keil采用"算术逻辑"进行编译


7
)位运算赋值运算符
          在对一个变量进行了位操作中,要将其结果再赋给该变量,就可以使用位运算赋值运算符。位运算赋值运算符如下:

&=, |=,^=,~=,<<=, >>=

例如:a&=b相当于a=a&b,a>>=2相当于a>>=2
  8)不同长度的数据进行位运算
          如果参与运算的两个数据的长度不同时,如achar型,bint型,则编译器会将二者按右端补齐。如果a为正数,则会在左边补满‘0’。若a为负数,左边补满‘1’。如果a为无符号整型,则左边会添满‘0’。
a=0x00; //a=0b 00000000
d=0xffff; //d=0b 11111111 11111111
d&=a; //a为无符号型,左边添0,补齐为0b 00000000 00000000d=0b 00000000 00000000


 

[C语言的移位操作]
一,
I=257 = 1 0000 0001  (二进制)
I=257/8 = 32.125 = 32   (I为int类型)

I=257>  >  3
=1 0000 0001>  >    3 = 0...010 0000=2^5=32
右移3位将将最后一个001移除了,1/8=0.125   所以这等价是没问题的


J=456=0...01 1100 1000
J=456%32=8;      (J为int类型)
J=456-(456>  >  4  <  <4);   
456>  >  4=0...01 1100 1000>  >  4
            =0...01 1100
然后再左移四位,则低四位补0:   0...01 1100 0000
上面两步实际上是将低四位变为了0,也就是由原来的1000变为现在的0000
所以现在数比移位操作前少了1000,也就是8
所以J=456-(456>  >  4  <  <4)=8

这里的求余运算我们还可以这样:
456=0...01 1100 1000=0x1c8
那么求456%32,就只要知道低5位是多大就OK了
所以456%32=0x1c8 & 0x01f,这样从第6位开始就全部变为0,余下的就是所求。



J=456=0...01 1100 1000
从上面的分析就知道
456>  >  3  <  <3比456而言就是低3位 000 变为现在的 000,
嘿嘿,没变,所以 k  = 456 - (456>  >  3  <  <3)=0

456>  >  4  <  <4比456而言就是低4位 1000 变为现在的 0000,
小了8,所以 l  = 456 - (456>  >  4  <  <4)=8

456>  >  5  <  <5比456而言就是低5位 01000 变为现在的 00000,
小了8,所以 m  = 456 - (456>  >  5  <  <5)=8

456>  >  6  <  <6比456而言就是低6位 001000 变为现在的 000000,
小了8,所以 n  = 456 - (456>  >  6  <  <6)=8

456>  >  7  <  <7比456而言就是低7位 1001000 变为现在的 0000000,
小了2^6+2^3=64+8=72,所以 o = 456 - (456>  >  7  <  <7)=72


 

c++位运算(收藏用)[url=] 楼主[/url]snowingbf(snowingbf)2004-10-19 00:03:07 在 C/C++ / C++ 语言 提问
前言   
  看到有些人对位运算还存在问题,于是决定写这篇文章作个简要说明。   
   
  什么是位(bit)?   
   
  很简单,位(bit)就是单个的0或1,位是我们在计算机上所作一切的基础。计算机上的所有数据都是用位来存储的。一个字节(BYTE)由八个位组成,一个字(WORD)是二个字节或十六位,一个双字(DWORD)是二个字(WORDS)或三十二位。如下所示:   
   
      0   1   0   0   0   1   1   1   1   0   0   0   0   1   1   1   0   1   1   1   0   1   0   0   0   1   1   1   1   0   0   0   
  |   |                             |                               |                               |                             |   |   
  |   +-   bit   31             |                               |                               |               bit   0   -+   |   
  |                                 |                               |                               |                                 |   
  +--   BYTE   3   ----   -+----   BYTE   2   ---+----   BYTE   1   ---+---   BYTE   0   -----+   
  |                                                                 |                                                                 |   
  +------------   WORD   1   ------------+-----------   WORD   0   -------------+   
  |                                                                                                                                   |   
  +-----------------------------   DWORD   -----------------------------+   
   
  使用位运算的好处是可以将BYTE,   WORD   或   DWORD   作为小数组或结构使用。通过位运算可以检查位的值或赋值,也可以对整组的位进行运算。   
   
  16进制数及其与位的关系   
  用0或1表示的数值就是二进制数,很难理解。因此用到16进制数。   
   
  16进制数用4个位表示0   -   15的值,4个位组成一个16进制数。也把4位成为半字节(nibble)。一个BYTE有二个nibble,因此可以用二个16进制数表示一个BYTE。如下所示:   
   
  NIBBLE       HEX   VALUE   
  ======       =========   
    0000                 0   
    0001                 1   
    0010                 2   
    0011                 3   
    0100                 4   
    0101                 5   
    0110                 6   
    0111                 7   
    1000                 8   
    1001                 9   
    1010                 A   
    1011                 B   
    1100                 C   
    1101                 D   
    1110                 E   
    1111                 F   
   
  如果用一个字节存放字母"r"(ASCII码114),结果是:   
  0111   0010         二进制   
      7         2           16进制   
   
  可以表达为:'0x72'   
   
  有6种位运算:   
        &       与运算   
        |       或运算   
        ^       异或运算   
        ~       非运算(求补)   
      >>       右移运算   
      <<       左移运算   
   
  与运算(&)   
  双目运算。二个位都置位(等于1)时,结果等于1,其它的结果都等于0。   
        1       &       1       ==       1   
        1       &       0       ==       0   
        0       &       1       ==       0   
        0       &       0       ==       0   
   
  与运算的一个用途是检查指定位是否置位(等于1)。例如一个BYTE里有标识位,要检查第4位是否置位,代码如下:   
   
  BYTE   b   =   50;   
  if   (   b   &   0x10   )   
          cout   <<   "Bit   four   is   set"   <<   endl;   
  else   
          cout   <<   "Bit   four   is   clear"   <<   endl;   
   
  上述代码可表示为:   
   
          00110010     -   b   
      &   00010000     -   &   0x10   
    ----------------------------   
          00010000     -   result   
   
  可以看到第4位是置位了。   
   
  或运算(   |   )   
  双目运算。二个位只要有一个位置位,结果就等于1。二个位都为0时,结果为0。   
        1       |       1       ==       1   
        1       |       0       ==       1   
        0       |       1       ==       1   
        0       |       0       ==       0   
   
  与运算也可以用来检查置位。例如要检查某个值的第3位是否置位:   
   
  BYTE   b   =   50;   
  BYTE   c   =   b   |   0x04;   
  cout   <<   "c   =   "   <<   c   <<   endl;   
   
  可表达为:   
   
          00110010     -   b   
      |   00000100     -   |   0x04   
      ----------   
          00110110     -   result   
   
  异或运算(^)   
  双目运算。二个位不相等时,结果为1,否则为0。   
   
        1       ^       1       ==       0   
        1       ^       0       ==       1   
        0       ^       1       ==       1   
        0       ^       0       ==       0   
   
  异或运算可用于位值翻转。例如将第3位与第4位的值翻转:   
   
  BYTE   b   =   50;   
  cout   <<   "b   =   "   <<   b   <<   endl;   
  b   =   b   ^   0x18;   
  cout   <<   "b   =   "   <<   b   <<   endl;   
  b   =   b   ^   0x18;   
  cout   <<   "b   =   "   <<   b   <<   endl;   
   
  可表达为:   
   
          00110010     -   b   
      ^   00011000     -   ^0x18   
      ----------   
          00101010     -   result   
   
          00101010     -   b   
      ^   00011000     -   ^0x18   
      ----------   
          00110010     -   result   
   
  非运算(~)   
  单目运算。位值取反,置0为1,或置1为0。非运算的用途是将指定位清0,其余位置1。非运算与数值大小无关。例如将第1位和第2位清0,其余位置1:   
   
  BYTE   b   =   ~0x03;   
  cout   <<   "b   =   "   <<   b   <<   endl;   
  WORD   w   =   ~0x03;   
  cout   <<   "w   =   "   <<   w   <<   endl;   
   
  可表达为:   
   
          00000011     -   0x03   
          11111100     -   ~0x03     b   
   
          0000000000000011     -   0x03   
          1111111111111100     -   ~0x03     w   
   
  非运算和与运算结合,可以确保将指定为清0。如将第4位清0:   
   
  BYTE   b   =   50;   
  cout   <<   "b   =   "   <<   b   <<   endl;   
  BYTE   c   =   b   &   ~0x10;   
  cout   <<   "c   =   "   <<   c   <<   endl;   
   
  可表达为:   
   
          00110010     -   b   
      &   11101111     -   ~0x10   
      ----------   
          00100010     -   result   
   
  移位运算(>>   与   <<)   
  将位值向一个方向移动指定的位数。右移   >>   算子从高位向低位移动,左移   <<   算子从低位向高位移动。往往用位移来对齐位的排列(如MAKEWPARAM,   HIWORD,   LOWORD   宏的功能)。   
   
  BYTE   b   =   12;   
  cout   <<   "b   =   "   <<   b   <<   endl;   
  BYTE   c   =   b   <<   2;   
  cout   <<   "c   =   "   <<   c   <<   endl;   
  c   =   b   >>   2;   
  cout   <<   "c   =   "   <<   c   <<   endl;   
   
  可表达为:   
          00001100     -   b   
          00110000     -   b   <<   2   
          00000011     -   b   >>   2   
   
  译注:以上示例都对,但举例用法未必恰当。请阅文末链接的文章,解释得较为清楚。   
   
  位域(Bit   Field)   
  位操作中的一件有意义的事是位域。利用位域可以用BYTE,   WORD或DWORD来创建最小化的数据结构。例如要保存日期数据,并尽可能减少内存占用,就可以声明这样的结构:   
   
  struct   date_struct   {   
          BYTE       day       :   5,       //   1   to   31   
                        month   :   4,       //   1   to   12   
                        year     :   14;     //   0   to   9999   
          }date;   
            
  在结构中,日期数据占用最低5位,月份占用4位,年占用14位。这样整个日期数据只需占用23位,即3个字节。忽略第24位。如果用整数来表达各个域,整个结构要占用12个字节。   
   
  |   0   0   0   0   0   0   0   0   |   0   0   0   0   0   0   0   0   |   0   0   0   0   0   0   0   0   |   
        |                                                           |                   |                     |   
        +-------------   year   --------------+   month+--   day   --+   
   
  现在分别看看在这个结构声明中发生了什么   
   
  首先看一下位域结构使用的数据类型。这里用的是BYTE。1个BYTE有8个位,编译器将分配1个BYTE的内存。如果结构内的数据超过8位,编译器就再分配1个BYTE,直到满足数据要求。如果用WORD或DWORD作结构的数据类型,编译器就分配一个完整的32位内存给结构。   
   
  其次看一下域声明。变量(day,   month,   year)名跟随一个冒号,冒号后是变量占用的位数。位域之间用逗号分隔,用分号结束。   
   
  使用了位域结构,就可以方便地象处理普通结构数据那样处理成员数据。尽管我们无法得到位域的地址,却可以使用结构地址。例如:   
  date.day   =   12;   
  dateptr   =   &date;   
  dateptr->year   =   1852;



 




Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐