KEIL里 Volatile的用法
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的用法的更多相关文章
- volatile的用法
在再有人问你Java内存模型是什么,就把这篇文章发给他中我们曾经介绍过,Java语言为了解决并发编程中存在的原子性.可见性和有序性问题,提供了一系列和并发处理相关的关键字,比如synchronized ...
- C#中volatile的用法
恐怕比较一下volatile和synchronized的不同是最容易解释清楚的.volatile是变量修饰符,而synchronized则作用于一段代码或方法:看如下三句get代码: int i1; ...
- Hibernate(或其它ORM)里的inverse用法详解,内容摘自Java web轻量级开发面试教程
本文来是从 java web轻量级开发面试教程从摘录的. Inverse的英文含义是反转,在Hibernate中用来决定是由哪方来维护两个业务实体类之间的关联关系,具体而言,就是由哪方去设置这个被外键 ...
- Linux编程里getopt_long_only函数用法详解
在程序中难免需要使用命令行选项,可以选择自己解析命令行选项,但是有现成的,何必再造轮子.下面介绍使用getopt_long_only和getopt_long(两者用法差不多)解析命令行选项. 程序中主 ...
- ABAP里SELECT的用法汇总(转)
通常使用Open SQL的数据查询语句SELECT将数据库条目选择到内存.一.SELECT语句:1)SELECT用于确定读取数据表中的哪些字段:2)FROM子句用于确定从哪些内表或者视图中读取数据:3 ...
- keil里面的包
1.使用Keil开发STM32程序时,需要下载STM32固件包 首先,keil就是一个编辑器,就跟记事本一样. 但keil还可以编译链接程序. 在创建一个工程时,要选择芯片序列. 要选择芯片序列的原因 ...
- Java volatile的用法---转载
我们知道,在Java中设置变量值的操作,除了long和double类型的变量外都是原子操作,也就是说,对于变量值的简单读写操作没有必要进行同步. 这在JVM 1.2之前,Java的内存模型实现总是从主 ...
- 【译】css动画里的steps()用法详解
原文地址:http://designmodo.com/steps-c... 原文作者:Joni Trythall 我想你在css 动画里使用steps()会和我一样有很多困惑.一开始我不清楚怎样使用它 ...
- SQL里IN的用法以及优化
1.in后条件不多,可以考虑主表建索引,或用union all 代替 2. in 和 exists的区别: 如果子查询得出的结果集记录较少,主查询中的表较大且又有索引时应该用in, 反之如果外层的主查 ...
随机推荐
- NoMachine 远程桌面控制
它是一个基于企业级对比套装的开源的终端服务器.它允许用户在连接速度缓慢或者窄带宽的情况下,对X11会话进行远程访问. NX项目提供一整套的运行库文件以及优化的来自X11,SMB,IPP,HTTP及其网 ...
- 深入super,看Python如何解决钻石继承难题
1. Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object): def __init_ ...
- 代理delegate、NSNotification、KVO在开发中的抉择
在开发ios应用的时候,我们会经常遇到一个常见的问题:在不过分耦合的前提下,controllers间怎么进行通信.在IOS应用不断的出现三种模式来实现这种通信: 1.委托delegation: 2.通 ...
- 关于bootstrap--网格系统
1. 2.偏移列(col-md-offset-*):为了在大屏幕显示器上使用偏移,请使用 .col-md-offset-* 类.这些类会把一个列的左外边距(margin)增加 * 列,其中 * 范围是 ...
- poj 3692 Kindergarten (最大独立集之逆匹配)
Description In a kindergarten, there are a lot of kids. All girls of the kids know each other and al ...
- 【Spark】Spark的Shuffle机制
MapReduce中的Shuffle 在MapReduce框架中,shuffle是连接Map和Reduce之间的桥梁,Map的输出要用到Reduce中必须经过shuffle这个环节,shuffle的性 ...
- swing入门例子
// a simple exmple that can show the basis of swing------------------------------------------------- ...
- std::remove_if
原型: #include <algorithm>forward_iterator remove_if( forward_iterator start, forward_iterator e ...
- 代码实现Layout android:layout_alignParentRight
代码实现Layout android:layout_alignParentRight 例如: android:id="@+id/account_option" android:la ...
- python-线程、进程、协程
进程 && 线程 进程:是内存中的一个独立的句柄,我们可以理解为一个应用程序在内存中就是一个进程. 各个进程之间是内存相互独立,不可共享的 线程:每个应用运行之后就会对应启动一个主线程 ...