在下面的代码片段中,中断例程使用许多数组中的一个来执行。使用的数组是同步选择的,不是异步(在ISR执行时它永远不会改变)。在单核微控制器上(如果架构很重要,这个问题假设为STM32L496),volatile
声明中是否需要foo
说明符?
int a[] = {1, 2, 3};
int b[] = {4, 5, 6};
int * foo; //int * volatile foo? int volatile * volatile foo?
main(){
disable_interrupt();
foo = a;
enable_interrupt();
...
disable_interrupt();
foo = b;
enable_interrupt();
}
void interrupt(){
//Use foo
}
我的假设是volatile
说明符不是必需的,因为任何foo
值的缓存都是正确的。
编辑:
为了澄清,最后的答案是volatile
或其他一些同步是必需的,因为否则可以省略或重新排序对foo
的写入。缓存不是唯一的问题。
答案 0 :(得分:1)
volatile
停止编译器优化它,强制编译器
在复杂的CPU(例如x86)上,CPU可以在易失性访问之前或之后重新排序操作。
它通常用于memory-mapped-io,其中内存区域实际上是设备,并且可以更改(即使在单核CPU上),没有明显原因。
C++11
的机制是使用std :: atomic来更改可能在不同执行线程上发生的值。
使用单核,代码将安全地修改该值并存储它。如果使用volatile,则在启用中断之前将其写入内存点。
如果你不使用volatile,那么代码在中断使用之前可能仍然在寄存器中有新值。
int * volatile foo;
描述foo可以改变,但它指向的值是稳定的。
int volatile * volatile foo
描述foo可以改变,它指向的东西也可以改变。我想你想要int * volatile foo;
对于怀疑volatile
是编译器障碍的人。
来自标准n4296
访问由volatile glvalue(3.10)指定的对象,修改对象,调用库I / O. 函数,或调用执行任何这些操作的函数都是副作用,这些是副作用 执行环境的状态。表达式(或子表达式)的评估通常包括 两个值计算(包括确定glvalue评估和提取的对象的身份 先前分配给对象进行prvalue评估的值)和副作用的启动。打电话的时候 到库I / O函数返回或评估对volatile对象的访问,考虑副作用 完成,即使调用隐含的某些外部操作(例如I / O本身)或volatile 访问权限可能尚未完成。
和
volatile对象 - 类型为volatile限定的对象,或volatile对象的子对象,或const-volatile对象的可变子对象。通过volatile限定类型的glvalue表达式进行的每次访问(读取或写入操作,成员函数调用等)都被视为可见的副作用,用于优化(即,在单个执行线程内,volatile访问无法优化或重新排序,具有在易失性访问之前排序或排序的另一个可见副作用。这使得易失性对象适合与信号处理程序通信,但不适用于另一个执行线程,请参阅std :: memory_order )。任何通过非易失性glvalue引用易失性对象的尝试(例如通过引用或指向非易失性类型的指针)都会导致未定义的行为。
这些似乎同意,存在编译器障碍,但与volatile对象交互的一些副作用可能尚未完成。对于单核处理器,如果C ++ 11原子不可用,它似乎是一种合适的机制。
我们有: -
与全表达相关的每个值计算和副作用在每个值之前排序 计算和副作用与下一个要评估的完整表达相关联。
据我所知,任何具有副作用的操作都存在happens-before
关系。
严格根据抽象机的规则评估对volatile对象的访问
据我所知,有规则(可能不透明)。
访问由volatile glvalue(3.10)指定的对象,修改对象,调用库I / O. 函数,或调用执行任何这些操作的函数都是副作用,这些是副作用 执行环境的状态。表达式(或子表达式)的评估通常包括 两个值计算(包括确定glvalue评估和提取的对象的身份 先前分配给对象进行prvalue评估的值)和副作用的启动。打电话的时候 到库I / O函数返回或评估对volatile对象的访问,考虑副作用 完成,即使调用隐含的某些外部操作(例如I / O本身)或volatile 访问权限可能尚未完成。
从中我了解到访问volatile(以及其他一些东西)会产生副作用,从而阻止编译器在易失性访问附近重新排序语句。