以文本方式查看主题

-  计算机科学论坛  (http://bbs.xml.org.cn/index.asp)
--  『 C/C++编程思想 』  (http://bbs.xml.org.cn/list.asp?boardid=61)
----  你今天volatile了吗?--慎重使用[原创]  (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=37312)


--  作者:dspfirmware
--  发布时间:8/28/2006 6:22:00 PM

--  你今天volatile了吗?--慎重使用[原创]
根据c/c++语法,const可以出现的地方,volatile几乎也都可以出现。但是,const修饰的对象其值不能改变,而volatile修饰的对象其值可以随意地改变,也就是说,volatile对象值可能会改变,即使没有任何代码去改变它。在这一点上,最典型的例子就是内存映射的设备寄存器和多线程中的共享对象。懂得使用volatile也是一门小小的艺术。使用volatile约束符可以阻止编译器对代码过分优化防止出现一些你意想不到的情况,达不到预期的结果;过频地使用volatile很可能会增加代码尺寸和降低性能。下面举个例子来说明volatile在优化中的微妙作用。

1.阻止编译器优化
  ARM Evaluator-7T模拟单机板使用基于内存映射的设备寄存器叫特殊寄存器,用来
控制和交互外围设备。CPU对内存的操作可以做到按位进行,而特殊寄存器是4字节对齐并占四个字节。你可以象unsigned int变量一样操作特殊寄存器(有些人可能更喜欢uint32_t,认为这样体现寄存器占用4个字节的特点。uint32_t在C99 头文件<stdint.h>中有定义)。而这里,为了体现寄存器本身作为寄存器的含义而非它的物理意义的,我们做如下定义:
typedef uint32_t special_register;

  Evaluator-7T板子上有一个按钮(可以认为是外设之一)。按下该按钮可以对IOPDATA寄存器第8位置1,相反,释放按钮会将该位重新清0。我们使用枚举方法为IOPDATA寄存器的第8位置定义一个掩码mask:
enum { button = 0x100 };

IOPDATA寄存器对应的地址为0x3FF5008,我们可以用宏形象地定义IOPDATA:

#define IOPDATA (*(special_register *)0x03FF5008)

有了这个定义,我们执行下面的循环就可以使CPU一直等待该按钮被按下:
while ((IOPDATA & button) == 0)
;

  然而这个期望必须建立在编译器不对代码进行优化的前提假设之上。如果编译器优化这段代码,那么它会认为在这个循环中没有什么会改变IOPDATA而且认为条件判断结果总是真或假,最终优化的结果是只对(IOPDATA & button)==0判断一次,之后的循环都不在对其进行判断,其等同于:
if ((IOPDATA & button) == 0)
for (;
;

  显然,如果条件判断结果为真(那么之后都会认为是真),那么这段代码将会陷入死循环。如果判断为假,那么循环就此结束。可以看出,优化的代码效率更高,因为每次循环相比原来的执行时间要短。不幸的是,这段优化代码使得它根本就不能响应按钮的每次动作。那么,如何解决这个问题呢?解决的关键就是不要让编译器优化这段代码,使用volatile就可以办到这一点。我们修改前面关于IOPDATA的宏定义:
#define IOPDATA (*(special_register volatile *)0x03FF5008)

这个定义将IOPDATA 定义为volatile类型的寄存器。volatile隐含地告诉编译器特殊寄存器可能会改变内容,即使没有任何显式地代码去改变它的内容。这样一来,编译器就不对IOPDATA作优化,而是每次都去访问IOPDATA,这其实正是我们所期望的。

2.无意中降低了效率
  有时候,如果不注意的话,使用volatile会无意中降低代码效率。举个例子。Evaluator-7T有一个七段数码显示器见下图:

图片无法在此显示,请访问 http://blog.sina.com.cn/u/4a317b790100050v
继续阅读文章.


W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
46.875ms