多くの低レベルプログラムは、メモリマッピングなどのタイプにvolatileキーワードを使用しますが、バックグラウンドで[〜#〜] really [〜#〜]が何をするかについて、ちょっと混乱しています。つまり、コンパイラがメモリアドレスを「最適化しない」とはどういう意味ですか?
volatile
は、他のプロセッサやI/Oデバイス、または何かが変数を変更できることを意味します。
通常の変数では、プログラムのステップがそれを変更する唯一のものです。したがって、たとえば、変数から5
を読み取り、それを変更しない場合でも、5
が含まれます。これを信頼できるので、次に変数を使用するときに、プログラムが時間をかけて変数を再度読み取る必要はありません。 C++コンパイラは、5
を記憶するだけのコードを生成するのに優れています。
しかし、それを5
として読み取ると、おそらくシステムがディスクからそのメモリにデータをロードし、500
に変更します。プログラムに新しい値500
を読み取らせたい場合は、以前に読み取った5
の使用について、コンパイラが賢くなりすぎないようにする必要があります。毎回値をリロードするように指示する必要があります。それがvolatile
が行うことです。
5歳児の例
大きな紙をテーブルの上に置いたとしましょう。紙の片隅に、進行中のゲームの現在のスコア3 to 4
を書き留めます。次に、テーブルの反対側に行き、ゲームについてのストーリーを書き始めます。ゲームを見ている友達は、ゲームが進むにつれてそのコーナーのスコアを更新します。彼女は3 to 4
を消去して3 to 5
を書き込みます。
ゲームのスコアをストーリーに入れるときは、次のいずれかを行うことができます。
3 to 4
を書き留めてください。変化がなかったと仮定して(または変化していてもかまいません)、または3 to 5
になった)を読み、戻ってください。これがvolatile
変数の働きです。volatile
は次の2つを意味します。
変数の値は、ユーザーのコードが変更することなく変更される場合があります。したがって、コンパイラが変数の値を読み取るときはいつでも、それはnotそれが最後に読み取られたときと同じであるか、または最後に格納された値と同じであると仮定しますが、もう一度読んでください。
揮発性変数に値を格納する動作は、「副作用」であり、外部から確認できるため、コンパイラーは値を格納する動作を削除できません。たとえば、2つの値が連続して格納される場合、コンパイラは実際に値を2回格納する必要があります。
例として:
i = 2;
i = i;
コンパイラ必須数値2を保存し、変数iを読み取り、読み取った変数をiに保存します。
別の状況があります:関数がsetjmp
を使用し、次にlongjmp
が呼び出された場合、関数のすべての揮発性ローカル変数は最後の値が格納されることが保証されます-これは、揮発性ローカル変数。
概要説明
CとC++の両方に抽象マシンの概念があります。コードがある変数の値を使用する場合、抽象マシンは実装がその変数の値にアクセスする必要があると言います。 _statement_A; statement_B; statement_C;
_形式のコードは、指定された順序どおりに実行する必要があります。これら3つのステートメントに共通する式は、出現するたびに再計算する必要があります。
抽象マシンごとに、一連のステートメント_statement_A; statement_B; statement_C;
_が与えられると、実装は最初に_statement_A
_全体を実行し、次に_statement_B
_、最後に_statement_C
_を実行する必要があります。実装では、age
に5の値を割り当てたことを思い出せません。 age
を参照するすべてのステートメントは、代わりにその変数の値にアクセスする必要があります。
実装が抽象マシン仕様に従ってCまたはC++コードを厳密に実行する場合は、volatile
キーワードは必要ありません。 CおよびC++の抽象マシンには、レジスタの概念、共通の副次式の概念はなく、実行順序は厳密です。
どちらの言語にもas-ifルールがあります。実装が動作する限り、その実装は標準に準拠していますあたかも抽象マシン仕様に従って処理を実行した。コンパイラーは、不揮発性変数が割り当て間で値を変更しないと想定できます。 _as-if
_ルールに違反しない限り、シーケンス_statement_A; statement_B; statement_C;
_は、_statement_C
_の一部、次に_statement_A
_の一部、次に_statement_B
_、残りの_statement_A
_、最後に残りの_statement_C
_。
これらのas-ifルールはvolatile
変数には適用されません。 volatile
変数と関数に関しては、実装は、指定したとおりに実行し、指定した順序で実行する必要があります。
抽象マシン仕様には欠点があります。遅いです。他の言語と比較したCおよびC++の1つの利点は、非常に高速であることです。これらの抽象マシンごとにコードが実行された場合、これは当てはまりません。 as-ifルールは、CおよびC++の高速化を可能にするものです。
ELI5回答
コンパイラがメモリアドレスを「最適化しない」とはどういう意味ですか?
メモリアドレスを「最適化」することは高度な概念であり、5歳児の能力の範囲内にはありません。従順な5歳児は、あなたが言うように、それ以上、それ以下ではありません。 volatile
を使用すると、実装は5つのように機能するように指示されます。代わりに、実装は、コードが指示するとおりに実行する必要があります。
(非)揮発性は、コンパイラーにコードを最適化する方法のヒントです(生成されたアセンブリコードの観点から):
答えはかなり一貫しているように見えますが、重要なポイントがありません。スペースを割り当てる必要があることをコンパイラーに伝え、すべてのアクセスに対してOR書き込み、そのアクセスを実行することを望んでいます。これらのアクセスやその変数を最適化することは望ましくありません。何らかの理由で。
はい、1つの理由は、他の誰かが私たちのためにその値を変更する可能性があるためです。別の理由は、他の誰かのためにその値を変更する可能性があることです。他の誰かが私たちのためにそれを変更するのか、私たちがそれを変更するのかは、ハードウェア/ロジックまたはソフトウェアかもしれません。ハードウェアへの書き込みまたはハードウェアからの読み取りを行う、ベアメタル組み込みプログラムの制御およびステータスレジスタへのアクセスを定義するためによく使用されます。他の答えで説明されているソフトウェアと話すソフトウェアと同様に。
コードのセクションの時間を計ろうとしているときに、アクセスが発生するタイミングと順序を制御するためにvolatileが使用されていることもわかります。また、volatileを使用しない場合は、問題の変数(開始時刻、終了時刻、および差分)のみを使用する必要があります。終わり近くに計算されたコンパイラーは、時間測定値のいずれかを(私たちが配置した場所ではなく)自由に移動できます。揮発性では不可能ですが、経験からは可能性が低くなっています。
時々、それが単に時間を燃やすために使用されるのを見るでしょう、エレメンタリーLEDウィンカー、ベアメタルのHello Worldは、人間の目がLEDを見るのに時間を費やすためだけに数に数える変数に揮発性を使用するかもしれません状態を変更します。タイマーやその他のイベントを使用して時間を消費する、より高度な例。