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, 反之如果外层的主查 ...
随机推荐
- fuck WPFG.org
今天一旦进入国外网站,就立刻跳转到WPFG.org.可能DNS被劫持污染了吧.用OpenDNS就行了.然后就没有这回事发生了 参考以下: https://www.opendns.com https:/ ...
- Linux下cut命令用法
1 一两句话描述一下cut命令吧! 正如其名,cut的工作就是“剪”,具体的说就是在文件中负责剪切数据用的. cut是以每一行为一个处理对象的,这种机制和sed是一样的.(关于sed的入门文章将在近期 ...
- 【转】Java如何克隆集合——深度拷贝ArrayList和HashSet
原文网址:http://blog.csdn.net/cool_sti/article/details/21658521 原英文链接:http://javarevisited.blogspot.hk/2 ...
- 怎样用jQuery自带方法/函数来获取outerHTML属性
原文地址:http://jingyan.baidu.com/article/7f41ececf93b48593d095c25.html 包括我自己在内(其实我也就这两天才知道这样可以快速获取的),很多 ...
- hdu3415:最大k子段和,单调队列
题目大意:给定长度为n的数组,求出最大的区间和,其中区间长度在[1,k]之间 分析: 学动态规划的时候我们会遇到一个经典问题 最大子段和,这个题跟最大子段和很类似 不同的是区间的长度有限制,无法用原算 ...
- cocos2d-x ios 设置横屏/竖屏(全)
Cocos2d-x项目\iOS\RootViewController.mm文件中. 以下方法任选其一即可… 本人机子函数二ok! 函数一: (BOOL)shouldAutorotateToI ...
- Linux磁盘及文件系统管理 2---- 使用fdisk进行磁盘管理
1 FDISK分区工具 1 fsidk是来自IBM的分区工具,支持绝大多数的操作系统,几乎所有的Linux都装有fdisk 2 fdisk是一个支持MBR的分区工具,如果要使用GPT的话我们无法使用f ...
- javascript笔记6之函数
/* function box() { //函数的声明 alert('我只有被调用才可以执行!'); //函数本身没有运行功能 } //必须调用才可以执行 box(); //调用 function b ...
- springmvc+mongodb+maven 项目搭建配置
操作步骤我就不再细化了 项目能运行,测试过了,先上配置,另一篇文章上代码,点击下载源码 项目结构 pom.xml <project xmlns="http://maven.apache ...
- mybatis学习笔记第一讲
第一步:先配置mybatis配置 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE confi ...