首先看一下单词“volatile”的释义:
volatile [ˈvɑlətl]
adj. 易变的,不稳定的; (液体或油)易挥发的; 爆炸性的; 快活的,轻快的;
下边是“C++ Primer”对volatile讲解的部分摘录:
“当一个对象的值可能会在编译器的控制或监测之外被改变时,该对象应该声明为volatile。因此,编译器执行的某些例行优化行为不能应用在已经指定为volatile的对象上……volatile修饰符的主要目的是提示编译器,该对象的值可能在编译器未监测到的情况下被改变。因此编译器不能武断地对引用这些对象的代码作优化处理。”
可见,修饰符volatile定义了一个“易变的、不稳定的、随时可能改变的”变量,对于被声明为volatile的变量的使用上跟普通的变量没有什么区别,最大的影响,就是编译器不能按照常规方式对其进行优化。
这就引入了两个问题:
编译器为何对访问变量的方式做优化以及如何优化?
编译器为何优化:
首先有这么个前提明确一下:编译器对变量的存取速度,寄存器快于内存,最慢是硬盘。
寄存器快于内存的主要原因体现在两者工作方式的差别上:
寄存器本身位于CPU内部,使用起来非常简单:第一,找到相关的位,第二,读取这些位,Over。
相比之下,内存的工作方式就复杂很多:
1.找到数据的指针(指针可能存放在寄存器内,所以这一步就已经包括寄存器的全部工作了。)
2.将指针送往内存管理单元(MMU),由MMU将虚拟的内存地址翻译成实际的物理地址。
3.将物理地址送往内存控制器(memory
controller),由内存控制器找出该地址在哪一根内存插槽(bank)上。
4.确定数据在哪一个内存块(chunk)上,从该块读取数据。
5.数据先送回内存控制器,再送回CPU,然后开始使用。
相对复杂的工作流程产生了更多的时延,累计起来就比寄存器慢很多,为了提高执行效率,编译器会对有必要优化的变量做访问方式上的处理,这就是编译器对变量的优化。
如何优化:
多数情况下,变量是存放在内存而非寄存器中的,这样对变量的存取效率很低。对于频繁使用的变量,编译器自动地把变量mov到寄存器里,使用的时候直接访问寄存器里的值,以加快存取速度,这就是寄存器对变量的优化。
早期C编译程序时不会把变量保存在寄存器中,除非显示使用关键字register修饰变量:
register long int value=123456789;
该关键字提醒编译器,所定义的变量会在程序中频繁被使用,建议编译器将其保存在CPU的寄存器中,以加快存取速度。其后随着编译技术的进步,编译器比程序员能更好的决定变量是应该存储在内存还是寄存器中,早在C++ 98/03标准中就明确,用register关键字声明的变量和不使用该关键字声明的变量一样,都具有自动存储期,现在在标准C++中,虽然还可以使用该关键字,但已经不再影响变量的实际定义。
对于被volatile 关键字修饰的变量,已经提前告知编译器该变量可能被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问,也就是说系统总是从它所在的内存地址读取数据,而非寄存器,并且使用完成之后立即按原路保存该变量的更改到内存。
volatile用法
<pre name="code" class="cpp">volatile long clock_register;
被volatile修饰的变量表示该变量会在意想不到的情况下改变,而const修饰的变量表示变量是不可改变的,那么一个变量能否同时使用volatile和const修饰呢?volatile const long clock_register;
const volatile long clock-register;
答案是肯定的,一个例子是只读的状态寄存器,用volatile修饰表示它可能会被意想不到的情况改变,这里是指编译器外部的情况,用const修饰表示在程序内部,不应该试图去人为修改它的值。