一、传统的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
上述方法在嵌入式系统的编程中是非常常见的,我们需要牢固掌握
将最右侧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,判断完以后,让前面的右移一位即可。
    
    对位的操作,一般程序中用的不多,但是在对计算机硬件操作时,肯定会涉及到。例如,我们以后要讲到的对串口和声卡操作就要用到一些。
   
| 在对单处机进行编程的过程中,对位的操作是经常遇到的。 C51 对位的操控能力是非常强大的。从这一点上,就可以看出 C 不光具有高级语言的灵活性,又有低级语言贴近硬件的特点。这也是在各个领域中都可以看到 C 的重要原因。在这一节中将详细讲解 C51 中的位操作及其应用。 1 、位运算符 C51 提供了几种位操作符,如下表所示: 
                 0&0=0; 0&1=0; 1&0=0; 1&1=1;                 
                  | 
一,
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
   
楼主
[/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;
   

 
