493

なぜvolatileCで必要なのですか? それは何のために使用されますか?それは何をしますか?

4

18 に答える 18

524

volatile変数に関係するものを最適化しないようにコンパイラーに指示しvolatileます。

これを使用する一般的な理由は少なくとも 3 つあります。これらはすべて、変数の値が目に見えるコードからのアクションなしで変更される可能性がある状況に関係しています。変数を使用する別のスレッドが実行されている場合。または、変数の値を変更する可能性のあるシグナル ハンドラーがある場合。

RAM のどこかにマップされ、コマンド ポートとデータ ポートの 2 つのアドレスを持つ小さなハードウェアがあるとします。

typedef struct
{
  int command;
  int data;
  int isBusy;
} MyHardwareGadget;

次に、いくつかのコマンドを送信します。

void SendCommand (MyHardwareGadget * gadget, int command, int data)
{
  // wait while the gadget is busy:
  while (gadget->isbusy)
  {
    // do nothing here.
  }
  // set data first:
  gadget->data    = data;
  // writing the command starts the action:
  gadget->command = command;
}

簡単に見えますが、データとコマンドが書き込まれる順序をコンパイラが自由に変更できるため、失敗する可能性があります。これにより、小さなガジェットが以前のデータ値でコマンドを発行するようになります。また、wait while busy ループも見てください。それは最適化されます。コンパイラは賢く、isBusy一度だけ値を読み取ってから無限ループに入ろうとします。それはあなたが望むものではありません。

これを回避する方法は、ポインターgadgetを として宣言することvolatileです。このようにして、コンパイラはあなたが書いたことを強制されます。メモリ割り当てを削除することも、変数をレジスタにキャッシュすることも、割り当ての順序を変更することもできません。

これは正しいバージョンです:

void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
{
  // wait while the gadget is busy:
  while (gadget->isBusy)
  {
    // do nothing here.
  }
  // set data first:
  gadget->data    = data;
  // writing the command starts the action:
  gadget->command = command;
}
于 2008-10-29T08:45:30.823 に答える
225

volatileC では、変数の値を自動的にキャッシュしない目的で実際に存在するようになりました。この変数の値をキャッシュしないようにコンパイラーに指示します。volatileしたがって、指定された変数に遭遇するたびに、メインメモリからその変数の値を取得するコードを生成します。このメカニズムが使用されるのは、いつでも値が OS または任意の割り込みによって変更される可能性があるためです。したがって、を使用volatileすると、毎回値に新たにアクセスするのに役立ちます。

于 2008-10-29T08:44:22.513 に答える
207

のもう 1 つの用途volatileは、シグナル ハンドラです。次のようなコードがある場合:

int quit = 0;
while (!quit)
{
    /* very small loop which is completely visible to the compiler */
}

コンパイラは、ループ本体が変数に触れていないことをquit認識し、ループをループに変換できwhile (true)ます。とquitのシグナルハンドラで変数が設定されていても; コンパイラはそれを知る方法がありません。SIGINTSIGTERM

ただし、quit変数が宣言されvolatileている場合、コンパイラは毎回それをロードする必要があります。これは、変数が別の場所で変更される可能性があるためです。これはまさにこの状況であなたが望むものです。

于 2008-10-29T10:52:39.553 に答える
63

volatile変数にアクセスしているコード以外の方法で変数が変更される可能性があることをコンパイラに伝えます。たとえば、I/O マップされたメモリ ロケーションである可能性があります。このような場合にこれが指定されていない場合、一部の変数アクセスを最適化できます。たとえば、その内容をレジスタに保持し、メモリ位置を再度読み込まないようにすることができます。

于 2008-10-29T08:41:28.803 に答える
31

Andrei Alexandrescu による記事「volatile - Multithreaded Programmer's Best Friend」を参照してください。

volatileキーワードは、特定の非同期イベントが存在する場合にコードが正しく表示されない可能性があるコンパイラの最適化を防ぐために考案されました。たとえば、プリミティブ変数を volatileとして宣言した場合、コンパイラはそれをレジスタにキャッシュすることはできません。これは、その変数が複数のスレッド間で共有されている場合に悲惨な結果をもたらす一般的な最適化です。したがって、一般的なルールは、複数のスレッド間で共有する必要があるプリミティブ型の変数がある場合、それらの変数を揮発性として宣言することです. しかし、実際にはこのキーワードを使用してさらに多くのことができます。これを使用して、スレッドセーフではないコードをキャッチすることができ、コンパイル時にそれを行うことができます。この記事では、その方法を示します。このソリューションには、コードの重要なセクションを簡単にシリアル化できる単純なスマート ポインターが含まれます。

この記事は と の両方Cに適用されC++ます。

Scott Meyers と Andrei Alexandrescu による記事「C++ とダブルチェック ロックの危険性」も参照してください。

そのため、一部のメモリ ロケーション (たとえば、メモリ マップ ポートまたは ISR [Interrupt Service Routines] によって参照されるメモリ) を処理する場合、一部の最適化を一時停止する必要があります。volatile は、そのような場所の特別な処理を指定するために存在します。具体的には、(1) volatile 変数の内容は「不安定」である (コンパイラが認識しない方法で変更される可能性がある)、(2) volatile データへのすべての書き込みは「監視可能」であるため、 (3) 揮発性データに対するすべての操作は、ソース コードに表示される順序で実行されます。最初の 2 つの規則により、適切な読み取りと書き込みが保証されます。最後の 1 つは、入力と出力を混在させる I/O プロトコルの実装を可能にします。これは、C および C++ の volatile が非公式に保証しているものです。

于 2010-07-22T12:33:43.130 に答える
19

volatile のわずかな用途は次のとおりです。関数の数値導関数を計算したいとしますf:

double der_f(double x)
{
    static const double h = 1e-3;
    return (f(x + h) - f(x)) / h;
}

問題は、一般に丸め誤差のために がx+h-x等しくないことです。h考えてみてください: 非常に近い数値を減算すると、多くの有効桁数が失われ、導関数の計算が台無しになる可能性があります (1.00001 - 1 を考えてください)。考えられる回避策は次のとおりです。

double der_f2(double x)
{
    static const double h = 1e-3;
    double hh = x + h - x;
    return (f(x + hh) - f(x)) / hh;
}

ただし、プラットフォームとコンパイラのスイッチによっては、積極的に最適化するコンパイラによって、その関数の 2 行目が消去される場合があります。だからあなたは代わりに書く

    volatile double hh = x + h;
    hh -= x;

コンパイラに hh を含むメモリ位置を強制的に読み取らせ、最終的な最適化の機会を失う。

于 2010-06-30T11:34:34.697 に答える
11

2つの用途があります。これらは特に組み込み開発でより頻繁に使用されます。

  1. コンパイラは、volatile キーワードで定義された変数を使用する関数を最適化しません。

  2. 揮発性は、RAM、ROM などの正確なメモリ位置にアクセスするために使用されます。これは、メモリ マップ デバイスの制御、CPU レジスタへのアクセス、および特定のメモリ位置の検索によく使用されます。

アセンブリ リストの例を参照してください。 Re: 組み込み開発における C の "volatile" キーワードの使用法

于 2011-07-30T04:59:31.793 に答える
10

揮発性物質が重要な別のシナリオについて言及します。

I/O を高速化するためにファイルをメモリ マップし、そのファイルが舞台裏で変更される可能性があるとします (たとえば、ファイルはローカル ハード ドライブ上になく、別のコンピュータによってネットワーク経由で提供されます)。

不揮発性オブジェクトへのポインターを介して (ソース コード レベルで) メモリ マップト ファイルのデータにアクセスすると、コンパイラによって生成されたコードは、ユーザーが気付かないうちに同じデータを複数回フェッチできます。

そのデータが変更された場合、プログラムは 2 つ以上の異なるバージョンのデータを使用するようになり、一貫性のない状態になる可能性があります。これにより、プログラムが論理的に正しくない動作をするだけでなく、信頼されていないファイルまたは信頼されていない場所からのファイルを処理すると、悪用可能なセキュリティ ホールが発生する可能性があります。

セキュリティを気にするなら、これは考慮すべき重要なシナリオです。

于 2011-11-26T10:05:46.443 に答える
9

Volatile は、特定のコード シーケンスを最適化しないようにコンパイラに強制する場合 (たとえば、マイクロ ベンチマークを作成する場合) にも役立ちます。

于 2008-10-29T08:46:43.380 に答える
7

volatile は、ストレージがいつでも変更される可能性が高く、変更される可能性が高いことを意味しますが、ユーザープログラムの制御外のものです。これは、変数を参照する場合、プログラムは常に物理アドレス (つまり、マップされた入力 fifo) をチェックし、キャッシュされた方法で使用しないことを意味します。

于 2011-07-09T03:14:54.377 に答える
6

私の意見では、 に過度の期待をするべきではありませんvolatile説明するために、 Nils Pipenbrinck の高投票の回答 の例を見てください。

彼の例はvolatile. コンパイラが有用で望ましい最適化を行わないようにするためvolatileだけに使用され ます。スレッドセーフ、アトミックアクセス、さらにはメモリの順序については何もありません。

その例では:

    void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
    {
      // wait while the gadget is busy:
      while (gadget->isbusy)
      {
        // do nothing here.
      }
      // set data first:
      gadget->data    = data;
      // writing the command starts the action:
      gadget->command = command;
    }

gadget->data = data前のみは、gadget->command = commandコンパイラによってコンパイルされたコードでのみ保証されます。実行時に、プロセッサは、プロセッサ アーキテクチャに関して、データとコマンドの割り当てを並べ替える可能性があります。ハードウェアが間違ったデータを取得する可能性があります (ガジェットがハードウェア I/O にマップされているとします)。データとコマンドの割り当ての間にメモリバリアが必要です。

于 2016-03-14T15:26:33.657 に答える
5

ウィキは次のことについてすべて述べていますvolatile

また、Linux カーネルのドキュメントでは、次の点についても優れた表記法が示されていますvolatile

于 2012-09-05T14:54:47.183 に答える
3

volatile は、コンパイルされたコードの外部から変更できます (たとえば、プログラムは volatile 変数をメモリにマップされたレジスタにマップする場合があります)。コンパイラは、volatile 変数を処理するコードに特定の最適化を適用しません。 t メモリに書き込まずにレジスタにロードします。これは、ハードウェア レジスタを扱う場合に重要です。

于 2008-10-29T08:45:31.760 に答える
-3

コンパイラが変数の値を自動的に変更することを許可しません。volatile 変数は動的に使用されます。

于 2010-05-21T19:23:27.940 に答える