volatile关键字:专一王子
2016-03-31 19:44
162 查看
每个变量和其名字一样善变,有时候它善变是发自内心的,有时候是外部因素决定的,只有volatile变量才会表里如一,因此获得了专一王子的美誉。
volatile字面意思是易挥发、易变化的意思,它修饰的变量表示该变量的值很容易由于外部的因素而发生改变,强烈要求编译器要老老实实的在每次对变量进行访问时去内存里读取。
举个生活中的栗子:
你明天 有一个朋友要过生日,今天把要送的礼物打包好了,一般情况下,我们明天起来不需要再次打开验证一下里面的礼物是否存在,因为我们知道,只要礼物的外包装没有动过,里面的东西应该也没有动过。其实编译器和人一样聪明,为了提高效率也会玩省事,做优化。
如下面的例子:
编译器扫描了代码发现上面,第一行代码将10赋给了整型变量a,之后a变量的值没有再发生变化,在后面的第二行中,将a变量的值取出来赋给b,在第三行代码里面将a变量的值赋给了c的时候,因为CPU访问内存速度慢,编译器为了提高效率,玩“省事”,直接将10赋给了c。
单从上面的代码我们来看是没有问题的,就如同从外包装看生日礼物完好一样。但是,如果上述代码运行在多线程中,在一个线程上下文中没有改变它的值,但是我们却不能保证变量的值没有被其他线程所改变,就好比是,生日礼物放在其他人那里保管,我们不敢100%的确定它里面的东西完好。当然这种数据不一致的机制不仅仅出现在多线程中,同样在设备的状态寄存器里也会存在。例如,网卡里的某状态寄存器里的值是否为1表示是否有网络数据到达,在当前时刻其值为1,不能代表下一时刻它的值还是1,它的值有外界决定,编译器肯定不能在这种情况下玩“省事”,为了防止在类似的情况下编译器玩省事,可以将这些变量声明为volatile,这样不管它的值有没有变化,每次对其值进行访问的时候,都会从内存里,寄存器了读取,保证数据的一致、做到表里如一。
总结:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
(1). 并行设备的硬件寄存器(如:状态寄存器)
(2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
(3). 多线程应用中被几个任务共享的变量
(1). 一个参数既可以是const还可以是volatile吗?解释为什么。
答:是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
(2). 一个指针可以是volatile 吗?解释为什么。
答:是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
(3). 下面的函数有什么错误:
这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
volatile字面意思是易挥发、易变化的意思,它修饰的变量表示该变量的值很容易由于外部的因素而发生改变,强烈要求编译器要老老实实的在每次对变量进行访问时去内存里读取。
举个生活中的栗子:
你明天 有一个朋友要过生日,今天把要送的礼物打包好了,一般情况下,我们明天起来不需要再次打开验证一下里面的礼物是否存在,因为我们知道,只要礼物的外包装没有动过,里面的东西应该也没有动过。其实编译器和人一样聪明,为了提高效率也会玩省事,做优化。
如下面的例子:
int a = 10; int b = a; int c = a;
编译器扫描了代码发现上面,第一行代码将10赋给了整型变量a,之后a变量的值没有再发生变化,在后面的第二行中,将a变量的值取出来赋给b,在第三行代码里面将a变量的值赋给了c的时候,因为CPU访问内存速度慢,编译器为了提高效率,玩“省事”,直接将10赋给了c。
单从上面的代码我们来看是没有问题的,就如同从外包装看生日礼物完好一样。但是,如果上述代码运行在多线程中,在一个线程上下文中没有改变它的值,但是我们却不能保证变量的值没有被其他线程所改变,就好比是,生日礼物放在其他人那里保管,我们不敢100%的确定它里面的东西完好。当然这种数据不一致的机制不仅仅出现在多线程中,同样在设备的状态寄存器里也会存在。例如,网卡里的某状态寄存器里的值是否为1表示是否有网络数据到达,在当前时刻其值为1,不能代表下一时刻它的值还是1,它的值有外界决定,编译器肯定不能在这种情况下玩“省事”,为了防止在类似的情况下编译器玩省事,可以将这些变量声明为volatile,这样不管它的值有没有变化,每次对其值进行访问的时候,都会从内存里,寄存器了读取,保证数据的一致、做到表里如一。
总结:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
(1). 并行设备的硬件寄存器(如:状态寄存器)
(2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
(3). 多线程应用中被几个任务共享的变量
(1). 一个参数既可以是const还可以是volatile吗?解释为什么。
答:是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
(2). 一个指针可以是volatile 吗?解释为什么。
答:是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
(3). 下面的函数有什么错误:
int square(volatile int *ptr) { return *ptr * *ptr; }
这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr) { int a,b; a = *ptr; b = *ptr; return a * b; }
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr) { int a; a = *ptr; return a * a; }
相关文章推荐
- java学习第四天
- 《学习javascript数据结构与算法》——第七章:字典和散列表
- cmd命令切换目录
- 【BZOJ1708】[Usaco2007 Oct]Money奶牛的硬币【完全背包】
- iptables简单介绍
- 利用yum 安装 lamp环境搭载 cacti监控和memcached数据库
- 实现一个线程池
- 【bzoj2816】[ZJOI2012]网络 LCT
- Android程序设计:实现手机和电脑文件的共享互传
- Qt之QProgressIndicator(等待提示框)
- Qt之QProgressIndicator(等待提示框)
- ALAsset,ALAssetsLibrary,ALAssetsgroup常见属性及用法
- iOS企业开发In House ipa发布流程
- IOS开发之UIView总结
- 如何仅通过CSS实现多行文本超长自动省略号
- 自制反汇编逆向分析工具 迭代第三版本
- 基于web的项目管理软件Redmine
- DOC与DOCX的区别
- acm 1017 装箱问题
- iOS开发之 cell上label出现文字重叠的情况