C でのプログラムの実行について読むときはいつでも、関数の実行についてほとんど話していません。プログラムが別の関数から呼び出されてから戻るまでに実行を開始したときに関数に何が起こるかをまだ調べようとしていますか? 関数の引数はどのようにメモリに格納されますか?
2 に答える
それは特定されていません。それは実装次第です。キース・トンプソンが指摘したように、それがどのように機能するかをあなたに伝える必要さえありません。:)
いくつかの実装はすべての引数をスタックに置き、いくつかはレジスターを使用し、多くはミックスを使用します(最初のn個の引数はレジスターに渡され、それ以上はスタックに入れられます)。
ただし、関数自体は単なるコードであり、読み取り専用であり、実行中に「発生」することはほとんどありません。
この質問に対する正しい答えは1つではありません。これは、プログラマーの作成者がこれを行うのに最適なモデルであると判断する方法に大きく依存します。このプロセスを説明する標準にはさまざまなビットがありますが、そのほとんどは実装定義です。また、プロセスは、システムのアーキテクチャ、目的のOS、最適化のレベルなどによって異なります。
次のコードを取ります:-
int DoProduct (int a, int b, int c)
{
return a * b * c;
}
int result = DoProduct (4, 5, 6);
MSVC2005コンパイラは、標準のデバッグビルドオプションを使用して、上記のコードの最後の行にこれを作成しました。-
push 6
push 5
push 4
call DoProduct (411186h)
add esp,0Ch
mov dword ptr [ebp-18h],eax
ここで、引数はスタックにプッシュされ、最後の引数から始まり、最後から2番目の引数が続き、最初の引数がスタックにプッシュされます。関数が呼び出され、引数がスタック(add esp,0ch
)から削除され、戻り値が保存されます。結果はeaxレジスタに格納されます。
関数のコードは次のとおりです。-
push ebp
mov ebp,esp
sub esp,0C0h
push ebx
push esi
push edi
lea edi,[ebp-0C0h]
mov ecx,30h
mov eax,0CCCCCCCCh
rep stos dword ptr es:[edi]
mov eax,dword ptr [a]
imul eax,dword ptr [b]
imul eax,dword ptr [c]
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
関数が最初に行うことは、ローカルスタックフレームを作成することです。これには、ローカル変数と一時変数を格納するためのスペースをスタックに作成することが含まれます。この場合、192(0xc0)バイトが予約されています(最初の3つの命令)。それが非常に多い理由は、編集と続行機能に新しい変数を入れるためのスペースを許可するためです。
次の3つの命令は、MSコンパイラによって定義された予約済みレジスタを保存します。次に、作成されたスタックフレームスペースが初期化され、特別なデバッグ署名(この場合は0xCC)が含まれます。つまりunitialised memory
、デバッグモードで0xCCだけで構成される値が表示された場合は、値を初期化するのを忘れています(0xCCが値でない限り)。
すべてのハウスキーピングが完了すると、次の3つの命令は、関数の本体である2つの乗算を実装します。その後、予約されたレジスタが復元され、スタックフレームが破棄され、最後に関数が。で終了しますret
。幸い、imul
は乗算の結果をeax
レジスタに入れるので、結果を正しいレジスタに入れるための特別なコードはありません。
さて、あなたはおそらく、本当に必要ではないことがたくさんあると思っていたでしょう。そして、あなたは正しいですが、デバッグはコードを正しくすることであり、上記の多くはそれを達成するのに役立ちます。リリースでは、取り除くことができることがたくさんあります。スタックフレームは必要ないため、初期化する必要はありません。予約済みのレジスタは変更されないため、保存する必要はありません。実際、コンパイラはこれを作成します:-
mov eax,dword ptr [esp+4]
imul eax,dword ptr [esp+8]
imul eax,dword ptr [esp+0Ch]
ret
これをコンパイラーに任せれば、呼び出し元にインライン化されます。
発生する可能性のあるものは他にもたくさんあります。レジスタに渡される値などです。また、関数との間で浮動小数点値と構造体/クラスがどのように受け渡されるかについても理解していません。そして、私がおそらく省略したものはまだまだあります。