volatileキーワードの使い方

4757 ワード

以下を転載しますhttp://blog.csdn.net/k346k346/article/details/46941497但し、若干の修正があります.
コードコンパイル環境:VS 2012 Win 32プラットフォーム
volatileは「変わりやすい」「不安定」という意味です.volatileは、C/C++の比較的少ないキーワードであり、変数が「共有」されている環境で発生しやすい読み込みエラーの問題を解決するために使用されます.
volatileがコンパイラに影響を与えた結果、volatile変数はいつでも変化する可能性があります.volatile変数に関する演算は、コンパイル最適化を行わないでください.エラーが発生しないようにしてください.例えば、volatile int i=10;int j=i;…int k=ivolatileは、コンパイラiがいつでも変化する可能性があると言っています.それを使うたびに、iのアドレスから読み出さなければならないので、コンパイラが生成した実行可能コードは、iのアドレスからデータを読み出してkに入れ直します.最適化は、コンパイラが2回のiからデータを読むコードの間にiを操作していないことを発見したため、自動的に最後にレジスタのデータをkに入れます.iのメモリから読み返すのではなく.このように、iがレジスタ変数であるか、ポートデータを示すとエラーが発生しやすいので、volatileは特殊アドレスへの安定したアクセスを保証し、エラーが発生しない.
1.volatileの役割
volatileと定義された変数は、この変数が予想外に変更される可能性があるということです.コンパイラは最適化の際に、この変数を使うたびに、RAMからこの変数の値を再読み込みします.レジスタに保存されたバックアップを使うのではなく、この変数を使います.
シングルタスクの環境では、関数の内部で、変数の値を2回読み込む間のステートメントが変数の値を変更していない場合、コンパイラは実行可能コードを最適化するように工夫します.アクセスレジスタの速度はRAM(RAMから変数の値を読み出してレジスタ)よりも速いので、以降は変数の値が変わらない限り、レジスタから変数の値を読み出してRAMにアクセスしない.
また、マルチタスク環境では、一つの関数体の内部では、変数の値を2回の読取変数の間で変更していないが、この変数は他のプログラム(中断プログラム、別のスレッドなど)によって変更される可能性がある.このカッタがRAMから変数の値を読み出すのではなくレジスタから読み込まれていると、修正されてもすぐに反応できないという問題があります.この現象を以下の手順でシミュレーションしました.
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
    int i=10;
    int a=i;
    cout<<a<<endl;
    _asm{
        mov dword ptr [ebp-4],80
    }
    int b=i;
    cout<<b<<endl;
    return 0;
}
プログラムはVSL 2012環境下でReleaseバージョンを生成し、出力結果は: 
10  10  上記の手順を読んで、次の点に注意してください.  (1)以上のコードはVsのreleaseモードで作成され、デフォルトの最適化オプションがあるべきです.コンパイルオプションのreleaseを修正したことがあるので、プログラムコードを最適化したいです.このような最適化は変数共有の環境で問題を引き起こしやすいです.
(2)語句b=i;以前、イントラネットのアセンブラコードによってiの値が修正されましたが、iの変化はbに反映されていません.iが複数のタスクに共有される変数であれば、このような最適化によるエラーは致命的かもしれません.
(3)アセンブリコード[ebp-4]は変数iのメモリアドレスを表しています.ebpは拡張ベースアドレスポインタレジスタであり、関数が属するスタックのアドレスを格納しています.まずスタックに入れて、4バイトを占有し、関数内で説明される局所変数が増加するにつれて、esp(スタックトップポインタレジスタ)は、スタックの成長方向が高いアドレスから低いアドレスに成長するため、対応する減少する.iは最初の変数で、4バイトを占有するので、iのアドレスは[ebp-4]である.
コンパイラが変数の読み込みに対してこのような最適化をどのように抑制し、エラーの読み込みを防止しますか?  volatileは簡単に適任できます.上のプログラムを少し修正し、変数iをvolatileとして前に申請すればいいです.次の手順を観察します.
#include <iostream>
using namespace std;
int main(int argc,char* argv[])
{
    volatile int i=10;
    int a=i;
    cout<<a<<endl;
    _asm{
        mov dword ptr [ebp-4],80
    }
    int b=i;
    cout<<b<<endl;
    return 0;
}
プログラム出力結果は以下の通りです. 
10  80  つまり、変数iの値を2回目に読み出すと、変化後の値が得られます.トラッキングコードから分かるように、volatileとして登録されている変数は、毎回メモリから変数の値を読み出します.場合によっては直接レジスタから値を取るのではありません.
2.volatile変数の適用の場合
(1)パラレルデバイスのハードウェアレジスタ(例えば、状態レジスタ).  (2)サービスを中断するサブルーチンで訪問する非自動変数(Non-auttic variables).  (3)マルチスレッドアプリケーションでは、いくつかのタスクによって共有される変数です.
上記の問題はC/C++プログラマと組み込みシステムプログラマを区別する最も基本的な問題です.組み込み作業をする人たちはハードウェア、中断、RTOSなどとよく付き合っています.これらはすべてvolatile変数を使う必要があります.volatileの内容がわからないと災難につながります.
次の質問は、面接者仲間がvolatileの重要性をまっすぐに知っているかどうかを見てもいいです.  1)一つのパラメータはconstでもvolatileでもいいですか?なぜですか  2)一つのポインターはvolatileでいいですか?なぜですか  3)下記の関数には何か間違いがありますか?
int square(volatile int *ptr) 
{ 
    return *ptr * *ptr; 
} 
以下は答えです 
1)はい.一例は読み出し専用の状態レジスタである.それはvolatileです.予想外に変更されるかもしれません.これはconstです.プログラムを修正するべきではないからです.
2)はい.これはあまりよくないですが.一つの例は、サービスサブルーチンが一つのバfferを指すポインタを修正するときである.
3)このコードはちょっと変態です.このコードの目的はポインタ*ptrの指し値の平方を返すことです.しかし、*ptrはvolatile型のパラメータを指すので、コンパイラは下記のようなコードを生成します.
int square(volatile int *ptr) 
{ 
int a,b; 
a = *ptr; 
b = *ptr; 
return a * b; 
} 
*ptrの値は予想外に変わるかもしれないので、aとbは異なるかもしれない.結果として、このコードは返されないかもしれません. 
あなたが望む二乗価値!正しいコードは以下の通りです.
long square(volatile int *ptr) 
{ 
int a; 
a = *ptr; 
return a * a; 
} 
3.埋め込み式プログラミングにおけるvolatileの役割
组み込みプログラミングではよくvolatileというキーワードを使っていますが、ネットで彼の使い方を调べたら次の2点にまとめられます.
(1)complerはどんな最適化もできないということを教えてください.  例えば、ある住所に二つの命令を送ります.
int *ip =...; //     
*ip = 1; //      
*ip = 2; //     
以上のプログラムはcomplerが最適化されている可能性があります.
int *ip = ...; 
*ip = 2; 
結果として最初のコマンドが失われました.volatileを使えば、complerはいかなる最適化も許されなくなります.それによってプログラムの原意を保証します.
volatile int *ip = ...;
*ip = 1;
*ip = 2;
comppilerを最適化したいとしても、2回の割当文を1つにしません.他の最適化しかできません.これはdevice driverプログラマにとって有用です.
(2)volatileで定義された変数はプログラム外で変更され、毎回メモリから読み込まなければならず、cacheやレジスターに入れて繰り返し使用できないことを示します.例えば:
volatile char a;   
a=0; 
while(!a)
{ 
  //do some things;   
}   
doother();
volatileがなければ、dootherは実行されません.
参考文献:
[1]]陳剛.C++高級進級教程[M].武漢:武漢大学出版社、2008.  [2]http://www.cnblogs.com/chio/archive/2007/11/24/970632.html  [3]http://www.360doc.com/content/13/0309/22/9290626_27068335.shtml