volatile用于防止相关变量被优化

  例如对外部寄存器的读写。对有些外部设备的寄存器来说,读写操作可能都会引发一定硬件操作,但是如果不加volatile,编译器会把这些寄存器作为普通变量处理,例如连续多次的对同一地址写入,会被优化为只有最后一次的写入。实际上,网卡的数据发送,就是按顺序连续往一个同地址写入数据,如果被优化,网卡将不能正常驱动。对于外部寄存器的读写,经常用 XBYTE,其实你看一下XBYTE的原型就知道了,里面也是有个volatile的。
  另一个使用场合是中断。如果一个全局变量,在中断函数和普通函数里都用到过,那最好对这个变量加volatile修饰。否则普通函数里,可能会仅从寄存器里读取这个变量以便加快速度,而不去实际地址读取该变量。

  很多人对Volatile都不太了解,其实Volatile是由于编译器优化所造成的一个Bug而引入的关键字。

;
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里读。

  volatile的本意是一般有两种说法--1.“暂态的”;2.“易变的”。这两种说法都有可行。但是究竟volatile是什么意思,现举例说明(以Keil-c与a51为例,例子来自Keil FQA),看完例子后你应该明白volatile的意思了,如果还不明白,那只好再看一遍了。
  例1.

void main (void)
{
      volatile int i;
      int j;

      i = ;    //1    不被优化 i=1
      i = ;    //2    不被优化 i=1
      i = ;    //3    不被优化 i=1

      j = ;    //4    被优化
      j = ;    //5    被优化
      j = ;    //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;
} 

  编译的汇编为:

 7E00      R       MOV       R6,#HIGH xdata_junk
 7F00      R       MOV       R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

 8F82              MOV       DPL,R7
 8E83              MOV       DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 注意
 E0                MOVX      A,@DPTR
 F500      R       MOV       t1,A

000B F500      R       MOV       t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                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;
} 

  编译的汇编为:

 7E00      R       MOV       R6,#HIGH xdata_junk
 7F00      R       MOV       R7,#LOW xdata_junk
;---- Variable 'p' assigned to Register 'R6/R7' ----

 8F82              MOV       DPL,R7
 8E83              MOV       DPH,R6

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 E0                MOVX      A,@DPTR
 F500      R       MOV       t1,A          ;a处

000B E0                MOVX      A,@DPTR
000C F500      R       MOV       t2,A
;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                RET 

  比较结果可以看出来,未用volatile关键字时,只从*p所指的地址读一次,如在a处*p的内容有变化,则t2得到的则不是真正*p的内容。
  例3:

volatile unsigned char bdata var;    // use volatile keyword here
sbit var_0 = ;
sbit var_1 = ;
unsigned ];

void main (void)
{
    unsigned char i;

    ; i < sizeof (values); i++)
    {
        var = values[i];
        if (var_0)
        {
            var_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++
//***************************************************************

{
    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. 完成
    );
}

//***************************************************************
// 函数: v_IntOccured()
// 版本:
// 参数:
// 用途: 死循环
//***************************************************************
void v_IntOccured()
{
    );
} 

  仿真可以看出,在没有用volatile时,即2处,程序不能进入v_IntOccured(); 但在4处可以进入v_IntOccured();

KEIL里 Volatile的用法的更多相关文章

  1. volatile的用法

    在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...

  2. C#中volatile的用法

    恐怕比较一下volatile和synchronized的不同是最容易解释清楚的.volatile是变量修饰符,而synchronized则作用于一段代码或方法:看如下三句get代码: int i1;  ...

  3. Hibernate(或其它ORM)里的inverse用法详解,内容摘自Java web轻量级开发面试教程

    本文来是从 java web轻量级开发面试教程从摘录的. Inverse的英文含义是反转,在Hibernate中用来决定是由哪方来维护两个业务实体类之间的关联关系,具体而言,就是由哪方去设置这个被外键 ...

  4. Linux编程里getopt_long_only函数用法详解

    在程序中难免需要使用命令行选项,可以选择自己解析命令行选项,但是有现成的,何必再造轮子.下面介绍使用getopt_long_only和getopt_long(两者用法差不多)解析命令行选项. 程序中主 ...

  5. ABAP里SELECT的用法汇总(转)

    通常使用Open SQL的数据查询语句SELECT将数据库条目选择到内存.一.SELECT语句:1)SELECT用于确定读取数据表中的哪些字段:2)FROM子句用于确定从哪些内表或者视图中读取数据:3 ...

  6. keil里面的包

    1.使用Keil开发STM32程序时,需要下载STM32固件包 首先,keil就是一个编辑器,就跟记事本一样. 但keil还可以编译链接程序. 在创建一个工程时,要选择芯片序列. 要选择芯片序列的原因 ...

  7. Java volatile的用法---转载

    我们知道,在Java中设置变量值的操作,除了long和double类型的变量外都是原子操作,也就是说,对于变量值的简单读写操作没有必要进行同步. 这在JVM 1.2之前,Java的内存模型实现总是从主 ...

  8. 【译】css动画里的steps()用法详解

    原文地址:http://designmodo.com/steps-c... 原文作者:Joni Trythall 我想你在css 动画里使用steps()会和我一样有很多困惑.一开始我不清楚怎样使用它 ...

  9. SQL里IN的用法以及优化

    1.in后条件不多,可以考虑主表建索引,或用union all 代替 2. in 和 exists的区别: 如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in, 反之如果外层的主查 ...

随机推荐

  1. Powershell 条件操作符

    Powershell 中的比较运算符-eq :等于-ne :不等于-gt :大于-ge :大于等于-lt :小于-le :小于等于-contains :包含-notcontains :不包含 进行比较 ...

  2. Qt编程之通过鼠标滚轮事件缩放QGraphicsView里面的Item

    首先自己subclass QGraphicsView的一个类,叫DiagramView,然后重新实现它的滚轮事件函数,然后发送一个缩放信号: oid DiagramView::wheelEvent(Q ...

  3. Linux环境下使用JFS文件系统

    Linux环境下使用JFS文件系统 JFS是IBM公司为linux系统开发的一个日志文件系统.从IBM的实力及它对Linux的态度来看,JFS应该是未来日志文件系统中最具实力的一个文件系统. JFS提 ...

  4. Jtree(节点的渲染+资源管理器)(2)

    上一次我们建立一个比较简单的资源管理器,这次我们说一下上面的资源管理器的问题,并且得尽量的贴近windows的资源管理器. 这样一个简单的资源管理树就完成了,下面我们说说它的问题: ① 图片和外观和W ...

  5. bzoj1624 [Usaco2008 Open] Clear And Present Danger 寻宝之路

    Description     农夫约翰正驾驶一条小艇在牛勒比海上航行.     海上有N(1≤N≤100)个岛屿,用1到N编号.约翰从1号小岛出发,最后到达N号小岛.一 张藏宝图上说,如果他的路程上 ...

  6. CSS flex让所有灵活的项目都带有相同的长度,忽略它们的内容:

    .flexbox {     display: -webkit-box;     display: -webkit-flex;     display: -ms-flexbox;     displa ...

  7. 深入理解linux网络技术内幕读书笔记(一)--简介

    Table of Contents 1 基本术语 1.1 本书常用的缩写 2 引用计数 2.1 引用计数函数 3 垃圾回收 3.1 异步 3.2 同步 4 函数指针 4.1 缺点 5 goto语句 5 ...

  8. join 数据库

    早上随手拿了本数据库的书,看到关于join的,想到很久之前妹妹(妹妹离职了,好桑感)发给我的一个简单浅显易懂的关于这方面的网页,所以翻出来瞅瞅,贴出来与大家共享之. http://coolshell. ...

  9. Windows下命令行直接编译程序

    D:\> cl hello.cpp Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86 Cop ...

  10. Hadoop Failed to set permissions of path

    在Eclipse里面写了个測试程序:把HDFS中的数据批量导入到HBase中 写好后,在本地測试遇到了例如以下问题: 14/04/21 16:49:53 WARN util.NativeCodeLoa ...