C语言之volatile
emOsprey 鱼鹰谈单片机 2月21日
预计阅读时间: 4 分钟
和 const 不同(关于 const 可以看 const 小节),当一个变量声明为 volatile,说明这个变量会被意想不到的改变。最为典型的就是 I/O 的输入寄存器了,因为这个变量的值和外部电平有关系,一旦外部电平发生了变化,那么这个变量也就跟着变。当然还有其它寄存器也是如此,比如各种状态寄存器、定时计数器等,他们的改变是靠硬件来改变的,你的程序只能读取数据,所以一定要申明为 volatile 才行,这样当你的优化级别提高的时候,你的程序也就能保证不会因为过度优化而出现问题。
那么申明为 volatile 的变量对编译器有什么影响呢?我们知道编译器是有优化功能的,很多时候,有些变量的值在运行过程可能是不变的,如果每次访问这个变量都要从原来的内存获得变量值,那是很浪费时间的,如果你让你的的编译器不做优化,那么它每次访问这个变量都会从内存读出数据,这样不仅效率不高,代码量也会比较大,而一旦进行优化了,编译器就会把它认为不变的量保存在内部寄存器中,每次访问这个变量的时候就访问这个寄存器就可以了,这样运行效率将大大提高。所以一般写代码的时候都会有两个版本,一个是 Debug 版本,一个是 Release 版本。 Debug 版本和 Release 版本其中的一个区别就是优化级别的不同,当然他们的不同不仅仅表现在这两个方面。
首先来看看两个不同优化等级的情况下的代码情况吧:
可以看到效果还是很明显的。
首先是 Code 减少了 24.1%,RO-data 不变,RW-data 减小了 8,这是因为我在程序中申明了两个指针在程序中并没有使用,所以被优化掉了。
就是这两货占用了 8 个字节空间,现在因为优化等级提高,被优化掉了。然后ZI-data 保持不变。
这样看来,优化效果还是很明显的。但是代码运行会不会出问题,很大程度上就是volatile 的问题了。
现在看看库函数的 GPIO 结构体声明:
可以看到每个寄存器声明都是 __IO,而 __IO 最终可以看到就是 volatile:
所以每次编译器碰到申明为 volatile 的变量就不敢用寄存器中的备份了,而是从原来的内存中访问数据。
除了硬件寄存器,还有多线程共享的变量和中断服务程序的使用变量,它们都是类似的道理,都是在一个函数中可能不改变,而在其他函数(中断处理函数或者其他线程的函数)可能改变的情况,如果不声明 volatile,那么编译器看到这个变量在一个函数中不改变,就可能会只访问一次内存,然后保存到寄存器中,使用时可能都只用寄存器的副本,而一旦其他程序也对这个变量进行了写操作,它是察觉不出来的,还可能破坏这次写操作,详细内容可以看信号量部分。
说到这里,就想到一个朋友遇到的情况。当时他有一个变量在中断处理程序中进行自加,而在 main 函数中进行读取,然后调试发现这个变量读出始终为 0,根本没有改变,但能确定已经进入中断处理程序了。当时我也知道这么个情况,但是我有其他事情,就没有深究。后来他调了半天加上了 volatile 关键字才解决,之后我问他是否是编译优化级别太高了,他说优化级别根本没动过,所以他只是怀疑,并没有真正去看他编译器的级别。等我叫他再去验证的时候发现真的是优化等级高了,根本不是 level 0,而是 level 3。
当然了,如果说你懂一点汇编语言,这个问题应该比较好解决,因为不管编译器怎么优化,最终都会在汇编层面得到体现,如果发现汇编代码始终使用寄存器的副本,那么就可以判定优化基本太高了,这个时候将优化级别设置为 level 0 就没有问题了,但是最好的办法还是加上 volatile,因为以后你可能会进行优化处理也说不定呢!
-THE END-
如果觉得文章对你有帮助,欢迎转发、分享给朋友,感谢你的支持!
C语言之volatile的更多相关文章
- C语言关键字-volatile
1.C语言关键字volatile C 语言关键字volatile(注意它是用来修饰变量而不是上面介绍的__volatile__)表明某个变量的值可能在外部被改变,因此对这些变量的存取 不能缓存 ...
- 从C语言的volatile关键字,了解C#的volatile机制(转载)
C#中有一个关键字volatile,一直不太明白到底什么时候才用它,只知道在多线程操作同一个变量的时候要使用volatile关键字,下面看到了一篇C语言关于volatile关键字的介绍,写的很不错,其 ...
- (转)c语言学习volatile
原文网址:http://www.cnblogs.com/chio/archive/2007/11/24/970632.html 参考网址:http://www.embedu.org/Column/Co ...
- c语言,volatile
一.意义: 该关键字的意义就是表示定义的变量值随时都会改变,必须从变量的地址处读取值,所以只有这个变量在使用过程中可能被改变(比如中断程序),就需要用这个关键字说明. )volatile, ...
- 面试问题之C++语言:volatile关键字的作用
volatile的作用 volatile关键字是防止在共享的空间发生读取的错误.只保证其可见性,不保证原子性:使用volatile指每次从内存中读取数据,而不是从编译器优化后的缓存中读取数据,简单来讲 ...
- [汇编与C语言关系]5. volatile限定符
现在研究一下编译器优化会对生成的指令产生什么影响,在此基础上介绍C语言的volatile限定符.首先看下面的C程序: /* artificial device registers */ unsigne ...
- C语言volatile关键字
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据.如果没有volatile关键字,则编译器可能优化读取和存储 ...
- zz剖析为什么在多核多线程程序中要慎用volatile关键字?
[摘要]编译器保证volatile自己的读写有序,但由于optimization和多线程可以和非volatile读写interleave,也就是不原子,也就是没有用.C++11 supposed会支持 ...
- C++ 中 volatile 的使用
一.作用 volatile的作用是: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值. 简单地说就是防止编译器对代码进行优化.比如如下程序:XBYTE[2]=0x55;XBY ...
随机推荐
- Collection集合常用的功能
package demo06; import java.util.ArrayList;import java.util.Collection; /** java.util接口 Collection&l ...
- [Agc029E]Wandering TKHS_树形dp_树上差分
Wandering TKHS 题目链接:https://atcoder.jp/contests/agc029/tasks/agc029_e 数据范围:略. 题解: 好神啊 Orz司队 https:// ...
- 网络编程之异步IO
Linux的I/O模型有下面几种:1. 同步阻塞I/O: 用户进程进行I/O操作,一直阻塞到I/O操作完成为止.2. 同步非阻塞I/O: 用户程序可以通过设置文件描述符的属性O_NONBLOCK,I/ ...
- python — 表的操作(一)
1. 创建表 创建表: create table t1 (id int,name char(4)); create table t2 (id int,name char(4)) engine=myis ...
- php curl post请求
/** * CreateBy Song * @param String $url url地址 * @param Array $post url参数 * @return Array */ functio ...
- Java生成随机数列表
生成随机数列表 1.Java8以前 (1)Math.random private List<UserEntity> random1() { ArrayList<UserEntity& ...
- springboot+mybatis调用oracle存储过程
1 存储过程参数为VARCHAR 代码逻辑:controller层定义实体类对象entity,并entity.set给存储过程的输入参数赋值,把赋值后的实体类通过service层传到dao层,然后通过 ...
- mac 在finder上面显示完成路径
打开终端,输入以下命令并回车: defaults write com.apple.finder _FXShowPosixPathInTitle -bool YES 然后再把finder关了再打开,你会 ...
- SIP中第三方呼叫控制(3PCC)建立流程
1.引言 在传统的电话网环境中,第三方呼叫控制允许一个实体(这里称为控制器- controller) 建立并管理另外的两方或多方之间的通信关系,而其本身并不参与通信. 第三方呼叫控制经常用于运营商业务 ...
- 使用css让表头固定的方法
1.可以使用display: table; width: 100%; table-layout: fixed; table-layout: fixed;设置表格布局算法.tableLayout 属性用 ...