4

Linux (または OS X) で関数呼び出しを行う場合、呼び出し先はスタック上の引数の値を変更できますか? 私は、呼び出し元がそれらをクリーンアップするものであるため、関数呼び出し後に同じ値を含める必要があると想定していました。ただし、-O2 を指定した GCC は、スタック上で渡されたパラメーターを変更していたことがわかりました。System V i386 呼び出し規則を含むドキュメントも探しましたが、これに対する決定的な答えを見つけることができませんでした。

これが私がデバッグしていたサンプルコードです。

pushl %eax       # %eax = 0x28
call _print_any
popl %eax
                 # %eax is now 0x0a

スタック上のそのパラメーターを変更するGCCは問題ないと思いますが、それができると指定されている場所を知りたいです。

4

4 に答える 4

3

(一部の呼び出し規則では) 呼び出し元が引数をクリーンアップしますが、実際に行っているのは、引数値を保持するためにスタックに割り当てられていたスペースの割り当てを解除することだけです。呼び出し元は後で値を見ることはないので、呼び出し先は関数の実行中に値を自由に変更できます。

投稿した例では、GCC はpopl %eax、スタック上のパラメーターによって占有されたスペースの割り当てを解除する命令を発行しました。実際に必要なのは %esp に 4 を加算することだけであり (x86 のスタックはメモリ内で下方に成長します)、popl %eax命令を実行することがこれを行うための最短かつ最速の方法です。poplコンパイラが 20 個の値の割り当てを解除する必要がある場合、コンパイラはおそらく 20 個の命令を発行する代わりに %esp を直接変更します。

次のコードでは、%eax の新しい値が使用されていないことに気付くでしょう。

于 2009-11-06T00:51:06.837 に答える
1

標準 C では、呼び出し先は必要に応じて引数の値を変更できますが、呼び出し元には変更が表示されません。

紛らわしいのは、POINTER を値に渡すと、呼び出し先はポインターを逆参照することでその値を変更できますが、呼び出し先が実際にポインター自体を変更しても、呼び出し元にはその変更が表示されないことです。

小さな問題: C 標準では、実装にスタックが必要でさえありません。

于 2009-11-06T00:52:50.377 に答える
1

はい、呼び出し先はスタック上の引数を変更できます。呼び出し先に関する限り、それらはローカル変数と同じです。呼び出し元はそれらをクリーンアップしますが、値を無視します。

C または C++ POD について話している場合、クリーンアップは単にスタック ポインターを変更することです。

デストラクタを使用して C++ について話している場合、呼び出し元はデストラクタを呼び出す責任がありますが、ジェネリック クラスのデストラクタは値をクリーンアップするために記述する必要があります。

于 2009-11-06T00:51:29.333 に答える
0

値渡しの場合:

call do_it(to_it);

引数はコピーされます (おそらくスタックの先頭にコピーされますが、コンパイラによってはそうではないかもしれません) セル化されたプログラムはこのコピーを好きなだけいじることができますが、clling プログラムの変数は変更されません。

参照渡しの場合:

call do_it(&to_it);

次に、変数のアドレスが渡されます。呼び出された変数が行う変更は、呼び出しプログラムの元の変数に反映されます。

于 2009-11-06T01:39:20.783 に答える