就像const一样,volatile是一个类型修饰符(type specifier)。它是被设计用来修饰被不同线程访问和修改的变量。如果没有volatile,基本上会导致这样的结果:要么无法编写多线程程序,要么编译器失去大量优化的机会。
例子1:
简单地说就是防止
编译器对代码进行优化.比如如下程序:
|
XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;
|
对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入
volatile,则编译器会逐一的进行编译并产生相应的机器代码(产生四条代码).
例子2:
1
|
for
(
int
i=0; i<100000; i++);
|
这个语句用来测试空循环的速度的
但是
编译器肯定要把它优化掉,根本就不执行
如果你写成
1
|
for
(
volatile
int
i=0; i<100000; i++);
|
它就会执行了
volatile的本意是“易变的”
1
2
3
4
5
6
7
8
9
10
11
|
staticinti = 0;
int
main(
void
)
{
//...
while
(1)
{
if
(i)dosomething();
}
}
/*Interruptserviceroutine.*/
voidISR_2(
void
){i=1;}
|
程序的本意是希望ISR_2中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在
main函数里面没有修改过i,因此
可能只执行一次对从i到某
寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被
例子3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
classGadget
{
public
:
void
Wait()
{
while
(!flag_)
{
Sleep(1000);
//sleeps for 1000milli seconds
}
}
void
Wakeup()
{
flag_=
true
;
}
//...
private
:
bool
flag_;
};
|
上面代码中Gadget::Wait的目的是每过一秒钟去检查一下flag_成员
变量,当flag_被另一个线程设为true时,该函数才会返回。至少这是程序作者的意图,然而,这个Wait函数是错误的。
例子4:
假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是真正懂得volatile完全的重要性。
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?如果不能,试回答存在什么问题:
1
2
3
4
|
int
square(
volatile
int
*ptr)
{
return
((*ptr) * (*ptr));
}
|
下面是答案:
1). 是的。一个例子是只读的
状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
关于Const:
如果
const int Max=100,那么 Max++会产生错误,因为用const修饰的变量被当作常量(并非肯定是个常量)使用,在同一个程序中不能对其进行赋值。但是const修饰的变量不允许这里修改不代表不允许别处修改,
比如下代码:
int i = 5;
const int* p = &i;
*p = 6; // 不可以;
i = 7; // 完全可以,而且那个“const”的“*p”也跟着变成了7。
对于非指针非引用的变量,const volatile同时修饰的意义确实不大。个人觉得。
int i = 5;
const int* p = &i;
*p = 6; // 不可以;
i = 7; // 完全可以,而且那个“const”的“*p”也跟着变成了7。
另外,
对于非指针非引用的变量,const volatile同时修饰的意义确实不大。个人觉得。
2). 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的
指针时。
1
2
3
4
5
6
7
|
int
square(
volatile
int
*ptr)
{
int
a,b;
a = *ptr;
b = *ptr;
return
a*b;
}
|
由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:
1
2
3
4
5
6
|
long
square(
volatile
int
*ptr)
{
int
a;
a = *ptr;
return
a*a;
}
|
一个定义为volatile的
变量是说这变量可能会被意想不到地改变,这样,
编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在
寄存器里的备份。下面是volatile变量的几个例子:
1)并行设备的硬件寄存器(如:状态寄存器);
存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;
2)一个中断服务子程序中会访问到的非自动变量(Non-automatic variables),即
中断服务程序中修改的供其它程序检测的变量需要加volatile;
3)
多任务环境下各任务间共享的标志应该加volatile,如多线程应用中被几个任务共享的变量