volatile的使用方法
来源:优易学  2011-12-15 12:05:37   【优易学:中国教育考试门户网】   资料下载   IT书店
  volatile的本意是一般有两种说法--1.“暂态的”;2.“易变的”。
  其实Volatile是由于编译器优化所造成的一个Bug而引入的关键字。
  int a = 10;
  int b = a;
  int c = a;
  理论上来讲每次使用a的时候都应该从a的地址来读取变量值,但是这存在一个效率问题,就是每次使用a都要去内存中取变量值,然后再通过系统总线传到CPU处理,这样开销会很大。所以那些编译器优化者故作聪明,把a读进CPU的cache里,像上面的代码,假如a在赋值期间没有被改变,就直接从CPU的cache里取a的副本来进行赋值。但是bug也显而易见,当a在赋给b之后,可能a已经被另一个线程改变而重新写回了内存,但这个线程并不知道,依旧按照原来的计划从CPU的cache里读a的副本进来赋值给c,结果不幸发生了。
  于是编译器的开发者为了补救这一bug,提供了一个Volatile让开发人员为他们的过失埋单,或者说提供给开发人员了一个选择效率的权利。当变量加上了Volatile时,编译器就老老实实的每次都从内存中读取这个变量值,否则就还按照优化的方案从cache里读。
  例1.
  void main (void)
  {
  volatile int i;
  int j;
  i = 1; //1 不被优化 i=1
  i = 2; //2 不被优化 i=1
  i = 3; //3 不被优化 i=1
  j = 1; //4 被优化
  j = 2; //5 被优化
  j = 3; //6 j = 3
  }
  例2.
  函数:
  void func (void)
  {
  unsigned char xdata xdata_junk;
  unsigned char xdata *p = &xdata_junk;
  unsigned char t1, t2;
  t1 = *p;
  t2 = *p;
  }
  编译的汇编为:
  0000 7E00 R MOV R6,#HIGH xdata_junk
  0002 7F00 R MOV R7,#LOW xdata_junk
  ;---- Variable ’p’ assigned to Register ’R6/R7’ ----
  0004 8F82 MOV DPL,R7
  0006 8E83 MOV DPH,R6
  ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 注意
  0008 E0 MOVX A,@DPTR
  0009 F500 R MOV t1,A
  000B F500 R MOV t2,A
  ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  000D 22 RET
  将函数变为:
  void func (void)
  {
  volatile unsigned char xdata xdata_junk;
  volatile unsigned char xdata *p = &xdata_junk;
  unsigned char t1, t2;
  t1 = *p;
  t2 = *p;
  }
  编译的汇编为:
  0000 7E00 R MOV R6,#HIGH xdata_junk
  0002 7F00 R MOV R7,#LOW xdata_junk
  ;---- Variable ’p’ assigned to Register ’R6/R7’ ----
  0004 8F82 MOV DPL,R7
  0006 8E83 MOV DPH,R6
  ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  0008 E0 MOVX A,@DPTR
  0009 F500 R MOV t1,A a处
  000B E0 MOVX A,@DPTR
  000C F500 R MOV t2,A
  ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  000E 22 RET
  比较结果可以看出来,未用volatile关键字时,只从*p所指的地址读一次
  如在a处*p的内容有变化,则t2得到的则不是真正*p的内容。
  例3
  volatile unsigned char bdata var; // use volatile keyword here
  sbit var_0 = var^0;
  sbit var_1 = var^1;
  unsigned char xdata values[10];
  void main (void) {
  unsigned char i;
  for (i = 0; i < sizeof (values); i++) {
  var = values[i];
  if (var_0) {
  var_1 = 1; //a处
  values[i] = var; // without the volatile keyword, the compiler
  // assumes that ’var’ is unmodified and does not
  // reload the variable content.
  }
  }
  }
  在此例中,如在a处到下一句运行前,var如有变化则不会,如var=0xff; 则在
  values[i] = var;得到的还是values[i] = 1;
  应用举例:
  例1.
  #define DBYTE ((unsigned char volatile data *) 0)
  说明:此处不用volatile关键字,可能得不到真正的内容。
  例2.
  #define TEST_VOLATILE_C
  // verwendete Include Dateien
  #if __C51__ < 600
  #error: !! Keil 版本不正确
  #endif
  // 函数 void v_IntOccured(void)
  extern void v_IntOccured(void);
  // 变量定义
  char xdata cvalue1; //全局xdata
  char volatile xdata cvalue2; //全局xdata
  // 函数: v_ExtInt0()
  // 版本:
  // 参数:
  // 用途:cvalue1++,cvalue2++
  void v_ExtInt0(void) interrupt 0 {
  cvalue1++;
  cvalue2++;
  }
  // 函数: main()
  // 版本:
  // 参数:
  // 用途:测试volatile
  void main() {
  char cErg;
  //1. 使cErg=cvalue1;
  cErg = cvalue1;
  //2. 在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++
  if (cvalue1 != cErg)
  v_IntOccured();
  //3. 使cErg=cvalue2;
  cErg = cvalue2;
  //4. 在此处仿真时手动产生中断INT0,使cvalue1++; cvalue2++
  if (cvalue2 != cErg)
  v_IntOccured();
  //5. 完成
  while (1);
  }
  // 函数: v_IntOccured()
  // 版本:
  // 参数:
  // 用途: 死循环
  void v_IntOccured() {
  while(1);
  }
  仿真可以看出,在没有用volatile时,即2处,程序不能进入v_IntOccured();
  但在4处可以进入v_IntOccured();

责任编辑:小草

文章搜索:
 相关文章
热点资讯
资讯快报
热门课程培训