在C ++中,volatile
的处理方式与const
相同:将指向易失性数据的指针传递给不希望volatile
修饰符触发编译错误的函数。 / p>
int foo(int* bar) { /* snip */ }
int main()
{
volatile int* baz;
foo(baz); // error: invalid conversion from ‘volatile int*’ to ‘int*’
}
为什么危险? const
修饰符很明显,删除它可以打破const
正确性;但是有“volatile
正确性”之类的东西吗?我无法弄清楚如何将指向易失性数据的指针作为指向非易失性数据的指针传递出来。
编辑以为你们知道为什么我首先使用volatile
:许多Mac OS X的OSAtomic
系列函数(用于原子增量,减量,添加,减少,比较和交换等)获取volatile
个参数。
答案 0 :(得分:9)
因为volatile
修饰符意味着编译器必须注意实际执行易失性数据项的每次读/写操作,就像C标准的“抽象机器”指定的那样。
当volatile
修饰符被剥离时,只要程序表现为“好像”访问发生,只要程序流程控制的单线程视点是,就可以优化数据访问。关心。换句话说,编译器可以处理非易失性数据,就像编译器和只有编译器可以看到并且可以修改数据项一样(在绝大多数情况下就是这种情况)。
volatile
关键字告诉编译器其他东西(硬件或其他执行线程)可以修改或查看该数据项,因此不允许编译器优化访问。
如果你可以将一个指向易失性数据的指针传递给一个没有警告的非易失性指针的函数,那么该函数可能看不到可能发生的数据的变化。如果您不关心这一点,您可以编写一个漂亮的便携式解决方法(取决于foo()
对数据的作用):
int foo(int* bar) { /* snip */ }
int main()
{
volatile int* baz;
int tmp = *baz;
foo(&tmp);
*baz = tmp;
}
答案 1 :(得分:5)
只要程序的顺序执行不受影响,编译器不仅可以优化对非易失性变量的访问,还可以预测性地/推测性地更新它们。
如果对volatile变量的虚假写入不会破坏您的设计,则可能不需要在任何上下文中使用volatile。
例如,C ++ 03编译器转换
是完全合法的int result;
void sum_if_all_positive( std::array<N> ary )
{
int sum = 0;
result = -1;
for( int i = 0; i < N; ++i ) {
if (ary[i] < 0) return;
sum += ary[i];
}
result = sum;
}
到
int result;
void sum_if_all_positive( std::array<N> ary )
{
result = 0;
for( int i = 0; i < N; ++i ) {
if (ary[i] < 0) { result = -1; return; }
result += ary[i];
}
}
(虽然这种改变提供的性能比仅在少数具有廉价存储器访问和极少数寄存器的架构上注册总和的性能更好。想到Microchip PIC架构。)
答案 2 :(得分:0)
好吧,在foo()
中,编译器不再知道baz
(或更严格地bar
)是不稳定的,因此可能会尝试应用一些不适当的优化。
答案 3 :(得分:0)
volatile
关键字表示应该每次从/向内存加载和存储该值。
考虑代码:
int foo(int* bar) {
while(*bar){
//Do something...
}
}
int main()
{
volatile int num = 1;
volatile int* baz = #
//Start a seperate thread to change *baz evenutally...
foo(baz);
}
当编译器看到while循环时,并且知道bar指向的内容总是为1.为什么每次都必须检查?每次检查都会非常浪费。 volatile
确保编译器每次都进行检查,因此当另一个线程更改该值时,while
循环退出。
答案 4 :(得分:-1)
它用于帮助确保多线程代码的安全性。