Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1682288
  • 博文数量: 311
  • 博客积分: 7778
  • 博客等级: 少将
  • 技术积分: 4186
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-09 19:59
个人简介

蓝点工坊(http://www.bluedrum.cn) 创始人,App和嵌入式产品开发。同时也做相应培训和外包工作。 详细介绍 http://pan.baidu.com/s/1y2g88

文章存档

2012年(3)

2011年(115)

2010年(170)

2009年(23)

分类: C/C++

2010-05-21 16:34:19

Andrew Huang
 
位操作是一个嵌入式C开发者需要掌握基本功之一.在寄存器,标志位操作等都需要大量位操作.
 
1.逻辑位操作
 
数C语言有几个标准的位操作符.(注意跟逻辑连接符 && || !区分开来)
  | 位或
  & 位与
  ~ 位取反
  ^ 位异或.
 
  这几个操作符也能与=结果成复合操作符 |=,&=,~=,^=.
 
1.1 ~取反操作
 

单目操作符.后面接一个整数类型.

 

格式: ~<数字变量| 数字常量>

 

给任何一个类型,按位取反.因此一个负数取反必须是一个正数.

 

 

简单判断一个数是否带符号位办法

 #define IS_SIGNED(n) ( (((n)>=0) && (~(n)<0)) || (((n)<0) && (~(n)>0)) )

这个宏就是利用补码取反符号位也改变特性

 

简单判断一个类型是否带符号位的办法

  #define   ISUNSIGNED(type)   (~((type)1)>0) 

 
 
  思考:能对浮点数double进行取反吗?后果是什么?
 
 产生编译错误,无法对应作取反操作
 
1.2 |位或
   
    双目操作符,格式: 数字 | 数字
     
   两个数字按位操作,每一个对应位只要出现1,结果必然是1,只有对应位上全0,结果为0,即满足如下运算
  1 | 0  = 1 ; 0 | 1 = 1 ; 1 | 1 = 1 ; 0 | 0 = 0

#define PRINT_INT(e) printf("%s=%d\n",#e, e)


   PRINT_INT(7 | 3 );
   PRINT_INT(7 | -1 );


 

1.3 &位与
   双目操作符,格式: 数字 & 数字
     
   两个数字按位操作,每一个对应位只要出现0,结果必然是0,只有对应位上全1,结果为1,即满足如下运算
  1 & 0  = 0 ; 0 & 1 = 0 ; 1 & 1 = 1 ; 0 & 0 = 0
 

void test5()
{
  int a = 7;
  PRINT_INT(4 & 3 );
  
  PRINT_INT(a &= 3);
    
}


 
1.4 ^ 异或操作
   对应两个位的数相同则值为9,两个位不相同则值为1,即顾名思意,只有两个数字相异才能或。
   1 ^ 0  = 1 ; 0 ^ 1 = 1 ; 1 ^ 1 = 0 ; 0 ^ 0 = 0.
 
   异或有一个特点,一个值与某个数作两次异或等于这个值本身
 

void test6()
{
   PRINT_INT( 7 ^ 2);
   
   PRINT_INT( 7 ^ 2 ^ 2);
}

 
2.移位操作
 

在C语言里,数字的右边是低位,左边是高位。因此左移是从低位向高位移动。用<<表示。右移是从高位向低位移动,用>>表示。

 

C语言移位要分为无符号数,有符号数两大类移位。,比较简单的无符号数移位,移为逻辑移位。逻辑左移是高位移走和低位补零.而逻辑右移是低位移动,高位取零。

 

(1)unsigned char x=3;

 

x<<1是多少?x>>1是多少?

 

有符号数的移位称为算术移位。因为牵涉到符号位,基本原则是保持符号位不变。其中算术左移是符号位不变,次高数丢弃,低位补0.

 

但是算术右移的情况就比较复杂了。标准C语言并没有规定算术右移,次高位需要补什么值。因此不同编译器表示不同行为。其中gcc的的规定是 低位丢弃,次高位被符号位。

 

 可以观察如下代码分析出编译器的补位的行为。

 

 

void test8()
{
  char x=-3;
  
  PRINT_INT(x >> 1);
  
  x = -3;
    PRINT_INT(x << 1);
  
}

 

3写成二进制数是00000011-3写成二进制数是(补码)11111101

 

 

 

程序执行的时候,操作的是数值的编码表示,也就是数值在内存中的二进制表示。比如说,程序取-3的时候,就去取11111101

 

 

(1)对无符号数3来说,x<<1往左移一位,最左边的位移掉了,最右边的移进来的位补零。变成00000110,所以结果是6x>>1往右边移一位,由于是无符号数,所以逻辑右移,最右边一位移掉,最左边移进来的位补零,变成00000001,所以结果是1

 

(2)对于有符号数3来说,x<<1往左移一位,最左边的位移掉了,最右边的移进来的位补零。变成00000110,所以结果是6x>>1往右边移一位,由于是有符号数,可能发生逻辑右移,也可能发生算术右移,这一点,C标准并没有明确地指定是使用逻辑右移还是算术右移。但大多数的机器都使用算术右移,变成00000001,所以结果还是1。但是请注意,这只是说大多数的机器是这样的,你敢保证自己不会碰到特殊情况吗?

 

(3)对于有符号数-3来说,x<<1往左移一位,最左边的位移掉了,最右边的移进来的位补零。变成11111010,结果是-6。往右移一位,由于是有符号数,可能发生逻辑右移,也可能发生算术右移。大多数机器使用算术右移,变成11111110,结果是-2

 

3.逻辑位操作应用

 

注意一般C语言的第几位,是以0为起始的.下列均是如此

 

3.1 清位操作

    清位操作是指把某几位置为0,其余位不变.

   

    清位操作要利用0与任意位相与均为0来完成,其余位不变则要利用1与任何相与值不变的特性.为了形成这样数,要把一个置0位为1数取反即可,即用 &=~操作.

  •     置第0位为0
             //1取反,表示0x11111110 ,与上 rRTCCON 表示把最低 0位置 0,其余位不变
             rRTCCON &= ~1 ;    //RTC read and write disable 
  • 置第三位为0
        //0x0100 取反。再与上相应的寄存器地址
         rRTCCON &= ~0x4; 
  • 同时清12,13位为0,

假设置  12-13 bit的为0 ,则二进制展开为  11000000000000,换算为  0x3000
     rADCDAT0 &=~0x3000;

  • 用移位来增加易读性

清位采用左移,或右移来置位,以置 14bit为例 ,这样将大大增强代码可读性.
     rADCDAT0 &=~(0x1<<14);

 

3.2 置位操作

   置位操作指把某几位置为1,其余位不变.

   置位操作是利用1与任意位来位或的特性来实现的

  •  置第0位为1
    //1取反,表示0x00000001 ,或上 rRTCCON 表示把最低 0位置 1,其余位不变
      rRTCCON |= 1 ;   //RTC read and write disable
  •  置第三位为1
         //0x0100,再或上相应的寄存器地址
            rRTCCON |= 0x4; 
  • 假设置  12-13 bit的为1 ,则二进制展开为  1100000000000
            rADCDAT0 |=0x3000; 
  • 置采用左移,或右移来置位,以置 14bit为 1例
            rADCDAT0 |=(0x1<<14); 
  •  

参见如下代码

  rIICCON = (1<<7) | (0<<6) | (1<<5) | (0xf);

这里的 0<<6会改变最终的值吗?写在这里是起什么作用?
 

3.3 取位操作

 取位操作是判断一个数的一些值,并且不能改变这个数.

  • 假设是否判断rADCDAT0的第 14位的值。
           if( rADCDAT0 & (0x01<<14)) .
                //值为1的操作
          else    //值为0的操作
  •  假设取  rADCDAT0的  12-13的值
                (rADCDAT0 & 0x3000)
  • 上述取值此在很多场合,得到是0和大于的1的非零值。但很多场合需要0,1的结果,如果处理?  

需要再额外加上一个判断表示式。才能得到结果

(rADCDAT0 & 0x3000)==0x3000

 

练习题

 1.在汇编语言还有一种叫作循环左/右移的操作,以循环左移为例,每一次移走的最高位移到最右的低上。循环右移刚好相反,请用C语言写一个无符号整数的循环左移和右移函数

2.请写三个宏,来分别实现清位,置位和取位操作

  CLR_BIT(val,num)

  SET_BIT(val,num)

  TEST_BIT(val,num)

 

 





阅读(2473) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~