volatile关键字有什么用?

在C / C ++中使用volatile关键字有什么用? 声明变量volatile和不声明变量之间有什么区别?

变量上的volatile限定符告诉编译器,无论何时访问此变量,其值都必须从内存加载,并且编译器可能不会假设它已经影响的先前存储的值。

因此,只要您遇到变量可能具有当前“执行线程”(广义上)无法预见的值的情况,它就是适当的。 这包括:

  • 硬件寄存器
  • 信号处理程序中的状态变量
  • 在意外跳转之后使用的实时变量,例如gotoswitch / case ,或者更重要的是setjmp / longjmp

volatile也是必要的(但不够!)用于对访问不是互斥的线程共享变量进行primefaces访问。 为此目的,即使只是为了阅读, volatile也绝不足以保证primefaces访问。 为此,您必须使用未通过当前C标准C99的抽象机器建模(或接口)的CPU的特殊指令。 下一个标准C1X应该具有这样的原语。

Volatile告诉编译器变量可能会在它不知情的情况下发生变化 – 因此它不应该对其进行优化。

我唯一需要它的时候是在ISA卡的时候,当你读取一个内存地址来从总线获取数据时。 编译器中还有一个错误,意味着挥发无效!

它在某些并行/multithreading代码中也很有用

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关键字。

对于大多数C程序,对象的目的是保存当前执行上下文(“线程”,CPU核心等)中代码编写的最后一个东西。 编译器跟踪对象内容的最简单方法是在存储中分配空间,并将该变量的读写处理为该存储的读写,但存储本身并不代表代码的用途 – 它只是代表达到目的的手段。 给定unsigned x; ,如果编译器看到x+=3; x+6; x+=3; x+6; ,生成代码的最简单方法是获取x,添加3,将结果存储到x,然后获取x,添加6,并存储该结果。 但是,只有当编译器不知道如何以其他方式实现相同的效果时,才需要中间加载和存储。 给定这样的代码的更好的编译器通常能够简化它以简单地添加9。

然而,特别是在嵌入式或系统代码中,程序的目的可以包括以特定顺序从某些存储位置加载和存储某些值。 许多真实世界的机器通过具有由某些对象的加载和存储触发的硬件来执行I / O,并且响应地执行各种动作。 例如,在适当的条件下使得任何发送给它的字符被传递到终端的对象并不罕见。 例如,将’H’然后’i’存储到SERTX可能会发送Hi 。 让编译器尝试简化或合并这样的序列(例如,决定省略’H’的存储)会使程序无用。 volatile限定符向编译器指示虽然可以自由地合并大多数对象的大多数访问,但是有一些访问需要精确地执行这些访问。 实际上,可以想象对于每个标量类型(例如“无符号”)都有函数

 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); 

实际上,执行易失性存储的机器代码在大多数系统上与普通存储的代码相同(并且不生成任何类型的函数调用),但只有生成最终机器代码的编译器部分才会“知道“那 – 其他一切都应该对代码进行处理,好像它是一个编译器一无所知的函数。

不幸的是,在“优化”的名义下,一些编译器已经停止将volatile访问视为对不透明函数的调用。 如果代码调用编译器内部工作的函数,编译器一无所知,它必须假定函数可以访问其地址曾暴露给外部世界的任何和所有对象。 将此类处理应用于volatile限定变量将允许代码在普通存储中构造对象,利用整合加载和存储的能力,将该对象的地址存储到volatile限定指针,触发输出其他一些其他进程。缓冲。 代码需要测试volatile限定对象以确保在对该存储执行任何其他操作之前已完全输出缓冲区的内容,但是在遵循“opaque function”模型的实现上,编译器将确保对存储的所有写入在触发输出操作的易失性存储之前在代码中发生的事实上将生成存储指令,该存储指令在易失性存储的指令之前。

一些编译器编写者认为让编译器假设在执行对volatile限定的访问之前不需要为非限定变量生成存储更有用。 当然,标准并不要求实现承认写入易失性变量可能触发可能影响其他事物的操作的可能性,但这并不意味着这种结构可能有用的系统的高质量编译器不应该支持它们。 不幸的是,标准的作者不希望强制在某些系统上有用的行为而不是其他系统,这一事实被解释为建议编译器编写者即使在它们有用的系统上也不应该支持这种行为。

请注意,在某些处理器上,确保一个CPU按特定顺序执行指令可能不足以确保该指令的效果按顺序发生。 一些处理器包括配置选项,以便保证内存的某些部分上的操作按顺序发生,即使这会降低执行速度,并且一些处理器包括控制执行顺序的其他方法,但是这些问题与volatile分开。 在质量实现上, volatile将确保处理器至少知道需要发生的所有读取和写入; 程序员可能需要执行额外的步骤以确保处理器实际按指示执行它们,但是如果编译器没有告诉处理器某事,那么告诉处理器在它执行任何其他操作之前完成所有待处理的操作都不会有任何好处。需要写。

Volatile告诉编译器变量的值可以从ex代码范围之外改变。

 { int x; int y; } 

x和y的值仅限于范围,但如果我们从外部某处修改其值,则应将其标记为volatile,因为编译器会将这些值保存在临时寄存器中并始终给出相同的值而不是给出修改的值值。

因此,在这种情况下,必须使用volatile来获取该特定变量的最新值。

volatile限制编译器不要优化对代码中声明为volatile的特定变量的使用。

变量与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; } 

在上面的代码编译器中假设变量‘a’的值总是为5,因此编译器将所有‘a’替换为5作为优化目的。

变量与volatile

但是一些变量值由外部中断改变了,所以这里我们总是需要直接从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 

当我们在那时使用简单的非易失性变量时,编译器会尝试优化代码,例如,如果你使用b = a * a * a; 那么它将被用作b = a * 3; 但是当你在声明变量“a”时使用volatile关键字时,则不会对“a”的值进行优化,并且每次都会从内存中获取值,因为volatile限定符将允许其他硬件以及改变变量值的过程。