在C / C ++中使用volatile关键字有什么用?声明变量volatile
与未将其声明为volatile
之间有什么区别?
答案 0 :(得分:25)
变量上的volatile
限定符告诉编译器,无论何时访问此变量,其值都必须从内存加载,并且编译器可能不会假设它已影响的先前存储中的此值。 / p>
因此,只要您遇到变量可能具有当前“执行线程”(广义上)无法预见的值的情况,它就是合适的。这包括:
goto
,
switch
/ case
,或者更重要的是,
setjmp
/ longjmp
。 volatile
对于访问未被互斥的线程共享变量的原子访问也是必要的(但还不够!)。为此目的,volatile
绝不足以保证原子访问,即使只是为了阅读。为此,您必须使用未通过当前C标准C99的抽象机器建模(或接口)的CPU的特殊指令。下一个标准C1X应该具有这样的基元。
答案 1 :(得分:9)
Volatile告诉编译器变量可能会在不知情的情况下发生变化 - 因此它不应该将其优化掉。
我唯一需要它的时候是在ISA卡的时候,当你读取一个内存地址来从总线获取数据时。编译器中还有一个错误,意味着挥发性无效!
它在某些并行/多线程代码中也很有用
答案 2 :(得分:4)
Volatile告诉编译器这个值可能会改变,编译器不应该对它进行任何优化。一个例子。
/** port to read temperature **/
#define PORTBASE 0x40000000
unsigned int volatile * const port = (unsigned int *) PORTBASE;
for(;;)
{
if(*port == 300)
{
/** shutdown the system **/
}
}
如果port不是volatile,那么编译器将假定该值不能更改。如果* port == 300,它将永远不会进行检查。但是,可以根据传感器更改该值。我们把volatile告诉编译器没有做任何优化。 Thumb规则是在使用内存映射寄存器时,其值可以根据情况而改变,然后使用volatile关键字。
答案 3 :(得分:1)
对于大多数C程序,对象的目的是保存当前执行上下文中的代码所写的最后一个东西("线程",CPU内核等)。编译器跟踪对象内容的最简单方法是在存储中分配空间,并将该变量的读写视为该存储的读写,但存储本身并不代表代码 - 它只是代表达到目的的手段。给定unsigned x;
,如果编译器看到x+=3; x+6;
,生成代码的最简单方法是获取x,添加3,将结果存储到x,然后获取x,添加6,并存储该结果。但是,只有当编译器不知道如何以其他方式实现相同的效果时,才需要中间加载和存储。给定这样的代码的更好的编译器通常能够简化它以简单地添加9。
但是,特别是在嵌入式或系统代码中,程序的目的可能包括以特定顺序加载和存储来自某些存储位置的某些值。许多真实世界的机器通过具有由某些对象的加载和存储触发的硬件来执行I / O,并且响应地执行各种动作。例如,在正确的条件下使得任何发送给它的字符被传递到终端的对象并不罕见。存储' H'然后'我'例如,对于SERTX,可能会发送Hi
。让编译器尝试简化或合并这些序列(例如,决定省略' H'的存储)将使程序无用。 volatile
限定符向编译器指示虽然可以自由地合并大多数对象的大多数访问,但有一些访问需要按照写入精确执行。实际上,可以想象对于每个标量类型(例如" unsigned")都有函数
int volatile_load_unsigned(unsigned volatile *p);
void volatile_store_unsigned(unsigned volatile *p, usigned value);
编译器一无所知的行为,因此编码如下:
extern volatile unsigned x;
x+=3;
x+=6;
将被编译器解释为等效于:
extern volatile int x;
volatile_store_unsigned(&x, volatile_load_unsigned(&x)+3);
volatile_store_unsigned(&x, volatile_load_unsigned(&x)+6);
实际上,执行易失性存储的机器代码在大多数系统上 与普通商店的代码相同(并且不生成任何类型 函数调用)但只是编译器生成的部分 最终的机器代码将会#34;知道"那 - 其他一切都应该对待 代码好像它是一个函数,其编译器一无所知 约。
不幸的是,在" optimization"的名义下,一些编译器已经停止
将volatile访问视为对不透明函数的调用。如果代码调用
一个函数,其内部工作编译器一无所知,它
必须假设该函数可以访问其地址的任何和所有对象
曾经暴露在外面的世界。应用这种治疗方法
volatile
- 限定变量将允许代码构造一个对象
普通存储,利用整合负载的能力
和商店,将该对象的地址存储到volatile限定的
指针,触发一些输出缓冲区的其他进程。码
需要测试volatile限定对象以确保内容
在做其他任何事情之前,已经完全输出了缓冲区
使用该存储,但是遵循" opaque函数"模型编译器将确保在触发输出操作的易失性存储之前在代码中发生的对存储的所有写入实际上将生成存储指令,该指令位于易失性存储的指令之前。
一些编译器编写者认为让编译器假设他们在执行对volatile限定的访问之前不需要为非限定变量生成存储更有用。当然,标准并不要求实现承认写入易失性变量可能触发可能影响其他事物的行为的可能性,但这并不意味着对于这样的结构可能有用的系统的高质量编译器不应该这样做。支持他们。遗憾的是,标准的作者并不想要强制在某些系统上有用的行为而不是其他系统,这一事实被解释为建议编译器编写者即使在系统中也不应该支持这种行为。它们很有用。
请注意,在某些处理器上,确保一个CPU按特定顺序执行指令可能不足以确保该指令的效果按顺序发生。某些处理器包含配置选项,以便保证内存的某些部分的操作按顺序发生,即使这会降低执行速度,并且某些处理器包含其他控制执行顺序的方法,但此类问题与volatile
分开。在质量实现上,volatile
将确保处理器至少了解所有需要发生的读写操作;程序员可能需要执行额外的步骤以确保处理器实际按指示执行它们,但是如果编译器没有被告知,那么在它执行任何其他操作之前,告诉处理器完成所有未完成的操作。需要写东西的处理器。
答案 4 :(得分:0)
Volatile告诉编译器变量的值可以从代码范围之外改变为ex。
{
int x;
int y;
}
x和y的值仅限于范围,但如果我们从外部某处修改其值,则应将其标记为volatile,因为编译器会将这些值保存在临时寄存器中并始终给出相同的值而不是给出修改后的值。
因此,在这种情况下,必须使用volatile才能获得该特定变量的最新值。
答案 5 :(得分:0)
volatile限制编译器不要优化对代码中声明为volatile的特定变量的使用。
变量与不稳定之间的差异
变量没有易失性
int main()
{
int a = 5;
while(a == 5) //compiler replace variable **'a'** with 5 as optimization purpose
{
}
if(a==5)//compiler replace **'a'** with 5 as optimization purpose
{
}
return 0;
}
在上面的代码编译器中假设变量&#39; a&#39; 的值总是为5,因此编译器会替换所有&#39; a&#39; < / strong>以5为优化目的。
易变的变量
但是一些变量值被外部中断改变了,所以我们总是需要直接从Memory获取变量值。为此我们使用 volatile 。
volatile int a=5;
while(a==5) //compiler will npt optimize the variable **'a'** and always get the value of **'a'** from Memory
答案 6 :(得分:-3)
当我们在那时使用简单的非易失性变量时,编译器会尝试优化代码,例如,如果你使用b = a * a * a;那么它将被用作b = a * 3;但是当你在声明变量“a”时使用volatile关键字时,那么优化不会发生在“a”的值,并且每次都会从内存中获取值,因为volatile限定符将允许其他硬件以及更改变量值的过程。