1、なぜ volatile を使うのか?

C/C++ における volatile キーワードは const と対応し、変数を修飾するために使われ、通常は言語レベルのメモリバリアを確立するために使用されます。これは BS が "The C++ Programming Language" で volatile 修飾子について説明したものです:

A volatile specifier is a hint to a compiler that an object may change its value in ways not specified by the language so that aggressive optimizations must be avoided.

volatile キーワードは型修飾子の一種であり、それで宣言された型の変数は、コンパイラが知らない何らかの要因(オペレーティングシステム、ハードウェア、他のスレッドなど)によって変更される可能性があることを示します。このキーワードで宣言された変数に遭遇すると、コンパイラはその変数にアクセスするコードを最適化しなくなるため、特殊なアドレスへの安定したアクセスを提供できます。宣言時の構文は次のとおりです:int volatile vInt;volatile で宣言された変数の値が要求される場合、システムは常にその変数が置かれているメモリからデータを再読み取りします。たとえその直前の命令がその場所からデータを読み取ったばかりであってもです。また、読み取られたデータはすぐに保存されます。例:

volatile int i=10;
int a = i;
...
// 其他代码,并未明确告诉编译器,对 i 进行过操作
int b = i;

volatile は、i がいつでも変化し得ることを示します。i を使用するたびに i のアドレスから読み取らなければなりません。そのため、コンパイラが生成するアセンブリコードは、i のアドレスからデータを再読み取りして b に格納します。一方、最適化を行う場合は、コンパイラが i から2回データを読み取るコードの間に i に対する操作がないことを検出すると、前回読み取ったデータを自動的に b に格納します。i から再読み取りはしません。これでは、i がレジスタ変数であるかポートデータを表す場合、エラーが発生しやすくなります。したがって、volatile は特殊なアドレスへの安定したアクセスを保証できると言えます。なお、VC 6 では、通常のデバッグモードではコード最適化が行われないため、このキーワードの効果は見えません。次に、アセンブリコードを挿入して、volatile キーワードの有無がプログラムの最終のなコードにとえる影響をテストします。以下のコードを入力してください:

例

#include <stdio.h> void main() { int i = 10; int a = i; printf("i = %d", a); // 以下のアセンブリ文の役割は、メモリ内の i の値を変更することです // しかし、コンパイラに知られないようにします __asm { mov dword ptr [ebp-4], 20h } int b = i; printf("i = %d", b); }

次に、Debug バージョンモードでプログラムを実行すると、出力結果は次のようになります:

i = 10
i = 32

次に、Release バージョンモードでプログラムを実行すると、出力結果は次のようになります:

i = 10
i = 10

出力結果から明らかなように、Release モードではコンパイラがコードを最適化し、2回目には正しい i の値が出力されませんでした。次に、i の宣言に volatile キーワードを追加して、どのような変化があるか見てみましょう:

例

#include <stdio.h> void main() { volatile int i = 10; int a = i; printf("i = %d", a); __asm { mov dword ptr [ebp-4], 20h } int b = i; printf("i = %d", b); }

Debug と Release の各バージョンでプログラムを実行すると、出力はどちらも次のようになります:

i = 10
i = 32

{'translated': '{'translated': 'これは、volatile キーワードがその役割を果たしていることを示しています。実際には、インラインアセンブリによるスタック操作という方法だけが、コンパイラが認識できない変数の変更というわけではありません。さらに多いのは、マルチスレッドが共有変数に同時アクセスする際に、あるスレッドが変数の値を変更した場合、その変更後の値を他のスレッドに visible にする方法です。一般的に、volatile は以下のような場所で使用されます:'}'}

  • 1) 割り込みサービスルーチン内で変更され、他のプログラムが検出するための変数には volatile を追加する必要があります;
  • 2) マルチタスク環境で各タスク間で共有されるフラグには volatile を追加すべきです;
  • 3) メモリマップされたハードウェアレジスタにも通常 volatile 指定を追加する必要があります。読み書きのたびに異なる意味を持つ可能性があるからです;

2、volatile ポインタ

const 修飾子と同様に、const には「定数ポインタ」と「ポインタ定数」という言い方がありますが、volatile にも対応する概念があります:

ポインタが指すオブジェクト(データ)が const または volatile である場合:

const char* cpch;
volatile char* vpch;

注意:VC の場合、この機能の実装は VC 8 以降で安全になります。

ポインタ自身の値(アドレスを表す整数変数)が const または volatile である場合:

char* const pchc;
char* volatile pchv;

注意:

  • (1) 非 volatile の int を volatile int に代入することはできますが、非 volatile オブジェクトを volatile オブジェクトに代入することはできません。
  • (2) 基本型以外にも、ユーザー定義型を volatile 型で修飾することができます。
  • (3) C++ では、volatile 識別子を持つクラスは、そのインターフェースのサブセット(クラスの実装者によって制御されるサブセット)にのみアクセスできます。ユーザーは const_cast を使用してのみ、型インターフェースへの完全なアクセスを得ることができます。さらに、volatile は const と同様に、クラスからそのメンバーへ伝播します。

3、マルチスレッドにおける volatile

一部の変数は volatile キーワードで宣言されます。2つのスレッドが特定の変数を両方使用し、その変数の値が変更される可能性がある場合、volatile で宣言する必要があります。このキーワードの役割は、最適化コンパイラが変数をメモリから CPU レジスタにロードするのを防ぐことです。変数がレジスタにロードされると、2つのスレッドの一方がメモリ内の変数を使用し、もう一方がレジスタ内の変数を使用する可能性があり、プログラムの誤動作を引き起こします。volatile の意味は、コンパイラがその変数を操作するたびに、既存のレジスタの値を使用するのではなく、メモリから実際に取得するようにさせることです。次のようになります:

volatile  BOOL  bStop  =  FALSE;

(1) あるスレッド内:

while(  !bStop  )  {  ...  }  
bStop  =  FALSE;  
return;    

(2) 別のスレッドで、上記のスレッドのループを終了させたい場合:

bStop  =  TRUE;  
while(  bStop  );  //等待上面的线程终止,如果bStop不使用volatile申明,那么这个循环将是一个死循环,因为bStop已经读取到了寄存器中,寄存器中bStop的值永远不会变成FALSE,加上volatile,程序在执行时,每次均从内存中读出bStop的值,就不会死循环了。

このキーワードは、あるオブジェクトの格納場所をレジスタではなくメモリ内に設定するために使用されます。一般的なオブジェクトは、コンパイラが命令の実行速度を上げるためにそのコピーをレジスタに置く可能性があるからです。例えば、次のコードの中では:

...  
int  nMyCounter  =  0;  
for(;  nMyCounter<100;nMyCounter++)  
{  
...  
}  
...

このコードでは、nMyCounter のコピーが何らかのレジスタに格納される可能性があります(ループ内では、nMyCounter のテストと操作は常にこのレジスタの値に対して行われます)。しかし、別のコードが次のような操作を実行する場合もあります:nMyCounter -= 1;この操作では、nMyCounter の変更はメモリ内の nMyCounter に対して行われるため、nMyCounter の変更が同期しないという現象が発生します。

原文URL:https://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777432.html