3

_cdecl呼び出し規約に関する記事で、ライターは次のように述べています。

ローカルストレージの解放関数がローカルの一時スペースを割り当てるとき、スタックポイントから必要なスペースの量をデクリメントすることによって割り当てます。このプロセスを逆にして、そのスペースを再利用する必要があります。これは通常、スタックポインターに、以前に減算されたのと同じ量を追加することによって行われますが、一連のPOP命令で同じことを実現できます。

私の質問は、「差し引かれたのと同じ量をスタックポインタに追加する」または「一連のPOP命令」ではなく、ESPを現在のEBP値に単純に設定できるかどうかです。

好き:

mov esp, ebp

後でこの関数のローカル変数の数を変更した場合、後で値を増やす必要がないため、私にとってはより良い方法のように思えます。

4

2 に答える 2

4

技術的には、これはスタックフレームの悪用であり、スタックの不均衡から生じるエラーをキャッチするためにありますが、完全に合法です。

ただし、割り当てが大きいかデバイスが組み込まれているためにスタックスペースが非常に限られている場合は、呼び出しのたびにスタックをクリーンアップする価値があることに注意してください。また、何かが間違った量の引数を使用していることがわかるため、デバッグが少し簡単になります。

また、誰かがあなたのコードを維持しようとしているなら、彼らはそれが非常に混乱することに気付くでしょう。

于 2012-02-28T08:09:41.007 に答える
2

実際、それはまさにleave命令が行うことであり、高級言語をサポートするために導入されました。ただし、あまり使用されません。ほとんどのコンパイラは、明示的なmov esp, ebp; pop ebpシーケンスを実行するだけです。この質問も参照してください。

ただし、「フレームポインタを省略」の最適化を実行できる場合もあります。これにより、EBPが解放されて汎用レジスタとして使用できるようになりますが、ユーザー(またはコンパイラ)は、関数全体でESPの変更を追跡し、変更される可能性のあるオフセットを使用してローカル変数または入力引数をアドレス指定する必要があります。その場合は、ポップまたは明示的な追加を使用して、ESPを元の値に復元してから返す必要があります。

上記は機能全体(つまり、プロローグ/エピローグ)に関係していることに注意してください。__cdecl関数の途中で特定の関数を呼び出す必要がある場合、ESPをEBP値に復元することはできません。これは、その値がローカル変数にスペースが割り当てられる前の関数の最初でのみ有効であるためです。ここには2つのアプローチがあります。

1)引数をプッシュし、呼び出し後にESPを復元します。

push offset msg
call _printf
pop ecx ; clobbers ECX but shorter than add esp, 4

2)引数を予約済みのスタックスロットに移動します。その場合、ESPを復元する必要はありません。

mov dword ptr [esp+0], offset msg
call _printf
; no need to change ESP

2番目のオプションを選択する場合は、それらのスロットにローカル変数を格納しないようにする必要があります。また、このようなmov命令は通常、プッシュよりもかなり長いため、コードサイズが問題になる場合は考慮する必要があります。

于 2012-02-28T12:08:20.700 に答える