16ページのItem2(#definesよりもconsts、enums、およびinlineを優先する)で、Scottは次のように述べています。
また、優れたコンパイラは整数型のconstオブジェクト用のストレージを確保しませんが...
分かりません。たとえば、constオブジェクトを定義すると、
const int myval = 5;
次に、コンパイラは値5を格納するために(intサイズの)メモリを確保しておく必要がありますか?
またはconstデータは特別な方法で格納されていますか?
これは私が思うコンピュータストレージの問題です。基本的に、コンピューターはconstオブジェクトをどのように格納して、ストレージが確保されないようにしますか?
また、優れたコンパイラは整数型のconstオブジェクト用のストレージを確保しませんが...
やや正しいステートメントは、コンパイラが整数型のconstオブジェクトに対してデータメモリを確保しないことです。つまり、コンパイラはプログラムメモリと交換します。フォンノイマンアーキテクチャでは2つのアーキテクチャに違いはありませんが、ハーバードなどの他のアーキテクチャでは、区別がかなり重要です。
何が起こっているのかを完全に理解するには、アセンブリ言語が処理のためにデータをロードする方法を思い出す必要があります。データをロードするには、2つの基本的な方法があります-特定の場所からメモリを読み取る(いわゆるdirectアドレッシングモード)、または命令自体の一部として指定される定数を設定する(いわゆる即時アドレッシングモード)。コンパイラがconst int x = 5
宣言とそれに続くint a = x+x
、2つのオプションがあります。
x
が参照されるたびに、値5の即時ロード命令を生成します最初のケースでは、x
からaccumulatorレジスタへの読み取り、x
の場所の値のアキュムレータへの追加、およびa
の場所。 2番目のケースでは、load immediateが5、add immediateが5に続き、a
の場所へのストアが続きます。一部のコンパイラは、定数をそれ自体に追加していることを理解し、a = x+x
からa = 10
、およびa
の場所に10を格納する単一の命令を生成します。
必ずしも。また、コンパイルされたコードでmyval
の代わりに生の値5のみを使用することもできます。
#define MYVAL 5
とconst int myval = 5
の違いは、前者の場合、プリプロセッサがソースコード内のMYVAL
に関するすべての記述を5
コンパイラがソースコードを表示できるようになるまで。後者の場合でも、選択肢があります。最適化されていないデバッグビルドの場合、コンパイラーは明示的にconst int
を割り当てる可能性があるため、デバッガーでは生の値5だけでなく、定数myval
を確認できます。
PéterTörökの回答から最初の文を盗みますが、詳しくは次のように説明します。必ずしもそうとは限りません。コンパイルされたコードでmyval
の代わりに生の値5を使用することもできます。
myval
をメモリに割り当てることで通常の変数のように処理すると、アーキテクチャやメモリの処理方法に応じて、非常に小さなものから深刻なものまで、パフォーマンスに影響を与える可能性があります。
そのように機能すると、コンパイラーは、「load register [〜#〜] r [〜#〜]の行に沿って、myval
"。命令のオペランドとしてのmyval
の場所。そのため、命令自体と同じデータブロックから直接取得されます。最近のCPUでは、この値は命令のプリフェッチにより、オンチップですぐに利用できます。アドレスを取得しても、CPUはメモリから値を取得する必要があります。場所がキャッシュ内で近くにある場合、それはすぐに行くかもしれませんし、そうでない場合、それほど速くは行かないかもしれません。 CPUは値を取得するためにオフチップにする必要があるだけでなく、そうすることで、後で戻す必要があるキャッシュから、より有用な他のデータにぶつかる可能性があります。メモリを仮想化するOSでプログラムが実行されている場合、その場所へのアクセスによりページ違反が発生し、必要なページがペリフェラルを介してRAMに読み込まれるまでプログラムがスリープ状態になる可能性があります(たとえば、ディスク)I/O、プログラムはコンテキストスイッチを介して再起動し、キャッシュメカニズムはそれを使って何をするかを行います。
定数値をオブジェクトコードにハードワイヤリングすることにより、コンパイラーは "load register [〜#〜] r [〜#〜]のような値5
。 "上記のメモリアドレスと同様に、5
は、命令のオペランドであり、同じ方法で使用できます(つまり、プリフェッチされます)。 CPUが5
レジスタに[〜#〜] r [〜#〜]を入力し、そのビジネスを開始します。通常、アドレスとレジスターは同じサイズであるため、命令が占めるバイト数に違いはなく、実際の実行は、メモリから何かを釣り出したときに発生する可能性のあるキャッシュミスやページフォールトの可能性がゼロです。
Péterが指摘したように、コンパイラはデバッグビルドでmyval
にスペースとシンボルを割り当てることができます。これを行ってもその値をハードワイヤリングしても問題はありません。値が何であっても同じであり、シンボルは実際に人間がデバッグに使用するためにそこにあるためです。
レジスターは本来整数であるため、これはレジスターに保持できる値にのみ適用されることに注意してください。他の定数はメモリに残ります。
引用が言うことは全く正しくありません。
優れたコンパイラはstatic const変数のストレージを確保しません。 const変数が静的ではなく、ファイルスコープ内にある場合、変数は別のコンパイルユニットから参照される可能性があるため、mustはストレージを確保します。リンク時の最適化により、リンカーmightは、ストレージを排除し、変数ifを参照する命令を書き換えることができるため、プログラムがへのポインターを生成しないことを証明できますその変数。
const int
の代わりに#define
を使用するより良い理由は、デバッガーがマクロを「参照」しないため、デバッガーで#defined
の値を検査できないことです。
変数 'myval'が使用されている場合は常に、コンパイラーは5を代入します。
コンパイラーはconst値を即値オペランドと見なします。イミディエートオペランドはデータストレージを必要としません。コンパイラーは次のものを処理できます。
int foo = myVal;
と同じ
int foo = 5;
値5はデータメモリに保存されず、命令シーケンスの一部として保存されます。
値のアドレスが取得される可能性がある場合、コンパイラはデータストレージを予約する必要があります。その場合でも、myValの値が使用されると、コンパイラーは即時操作を引き続き使用します。