私は最近、コンパイル時にコンパイラが変数を解決することについて議論しているテキスト「Deep C Secrets」に出くわしました。グローバル変数と静的変数はプログラムの最後までスペースを占有するため、これは可能ですが、スタック上のスペースを取得するローカル変数の場合はどうなりますか? それらは実行時に割り当てられたスペースを取得しますか?もしそうなら、コンパイラはどのようにそれらのアドレスを追跡しますか?
3 に答える
まあ、ローカル変数は、グローバル/静的変数とは異なる動作をするだけです。
ローカル変数はスタックに「割り当て」られます。これは、実行中のプログラムに対してシステムによって割り当てられたメモリのチャンクです。スタック ポインターと呼ばれる、そのスタックを指す CPU によって保持される "ポインター" があり、一部のコンパイラ/CPU の "マジック" は、関数呼び出しでそのポインターを更新します。
最後に、「スタック ポインター」のようなものは、関数呼び出しごとにプライベートな紙切れのように、関数呼び出しごとにローカル メモリ チャンクを指し示すため、関数はそれを使用して他の場所には表示されないメモを取ることができます。したがって、ローカル変数は実行時に決定されるため、コンパイラは実際にはローカル変数の「アドレス」を処理しません。代わりに、コンパイラはその「紙片」上のローカル変数の場所を「追跡」します。言い換えると、ローカル変数の位置は「スタック ポインターに相対的」であるか、「スタック ポインターへのオフセット」として保持されます。
あなたの質問は興味深いものです。ここでは一般的な話をしていますが、これは私が覚えている限り真実です。その秘密は、コンパイラがコンパイル時に、すべての呼び出しルーチンとすべての呼び出されたルーチンを同じプロトコルに従うように強制することです。それが、これをすべて機能させるものです。
コンパイラは assy コードを出力して、呼び出しルーチンが関数パラメーターをスタックにプッシュするようにします (おそらく、リストされている順序で)。次に、呼び出し元のルーチンが呼び出されたルーチンにジャンプし、戻りアドレス (関数呼び出しの後のステートメント) をスタックに残します。
コンパイラは、呼び出されたルーチンに対して同様の assy コードを出力して、プッシュされたのと同じ順序でスタック上のこれらのパラメーターを検索します。呼び出されたルーチンがパラメーターの使用を終了すると、パラメーターをスタックからポップします。戻りアドレスをスタックからポップします (保持します)。回答 (関数の戻り値の型) をスタック (呼び出し元のルーチンが検索する場所) にプッシュしてから、戻りアドレスにジャンプします。
呼び出しルーチンは、スタックから回答を取り出し、それを使用します。
したがって、これらの関数パラメーターのプロセス全体は再配置可能です。
ちなみに、すべての「ローカル」変数 (呼び出された関数内) もスタック上に存在しますが、呼び出された関数が実行されている間だけです。呼び出された関数は、そのスペースを一時的に借ります (戻るまで)。これらのローカル変数がどこにあるかを知っているのは関数だけです。これが、関数が戻った後にローカル変数が失われる理由です。
大丈夫だと思います。
それらのアドレスは、関数呼び出しが実行されるたびに移動されるスタック ポインター (実際にはほとんどの場合ベース ポインター、IIRC) に相対的です (inline
もちろん関数を除く)。
ローカル変数を読み取るために、生成されたアセンブリは次のようになります。
mov eax, [ebp - 4]
ここで、 は(拡張 (32 ビット) ベース ポインター)-4
に対する変数の位置です。ebp
2 つのint
変数 (したがって 2 つの 32 ビット変数) がある場合、それらの位置は-8
andになります-4
(X86 アーキテクチャでは 32 ビットは 4 バイト幅であるため)。
疑わしい場合は、短いプログラムを作成し、それをアセンブリにコンパイルするのが最善の方法です。
gcc -S program.c -o output.S
cat output.S
アセンブリ レベルでの関数呼び出しの詳細については、この wikibookを参照してください。