10

Cの関数に引数を渡すことがどのように機能するのか知りたいです。値はどこに保存され、どのように取得されますか?可変引数の受け渡しはどのように機能しますか?また、関連しているので、戻り値はどうですか?

私はCPUレジスタとアセンブラの基本的な知識を持っていますが、GCCが私に吐き出すASMを完全に理解するには十分ではありません。いくつかの簡単な注釈付きの例をいただければ幸いです。

4

5 に答える 5

18

このコードを検討する:

int foo (int a, int b) {
  return a + b;
}

int main (void) {
  foo(3, 5);
  return 0;
}

でコンパイルするとgcc foo.c -S、アセンブリ出力が得られます。

foo:
    pushl   %ebp
    movl    %esp, %ebp
    movl    12(%ebp), %eax
    movl    8(%ebp), %edx
    leal    (%edx,%eax), %eax
    popl    %ebp
    ret

main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $8, %esp
    movl    $5, 4(%esp)
    movl    $3, (%esp)
    call    foo
    movl    $0, %eax
    leave
    ret

したがって、基本的に、呼び出し元(この場合main)は、最初にスタックに8バイトを割り当てて、2つの引数に対応し、次に2つの引数を対応するオフセット(4および0)でスタックに配置します。次にcall、制御をに転送する命令が発行されます。fooルーティーン。ルーチンはfoo、スタック内の対応するオフセットから引数を読み取り、それを復元しeax、呼び出し元が使用できるように戻り値をレジスターに入れます。

于 2010-12-09T07:28:15.540 に答える
5

これはプラットフォーム固有であり、「ABI」の一部です。実際、一部のコンパイラでは、さまざまな規則から選択することさえできます。

たとえば、Microsoft の Visual Studio は、レジスタを使用する __fastcall 呼び出し規約を提供しています。他のプラットフォームまたは呼び出し規約は、スタックを排他的に使用します。

可変引数は非常によく似た方法で機能します。レジスタまたはスタックを介して渡されます。レジスターの場合、通常、タイプに基づいて昇順になっています。(int a, int b, float c, int d) のようなものがある場合、PowerPC ABI はar3、br4、dr5、およびcfp1 に入れる可能性があります (float レジスターの開始位置を忘れましたが、アイデアはわかります)。 .

戻り値も同じように機能します。

残念ながら、私は多くの例を持っていません。私のアセンブリのほとんどは PowerPC にあります。アセンブリに表示されるのは、r3、r4、r5 を直接処理し、r3 にも戻り値を配置するコードだけです。

于 2010-12-09T07:17:32.683 に答える
2

あなたの質問は、誰もが SO の投稿で合理的に答えようとする以上のものであり、実装も定義されていることは言うまでもありません。

ただし、x86 の回答に興味がある場合は、この Stanford CS107 Lecture というタイトルのProgramming Paradigmsを視聴することをお勧めします。ここでは、最初の 6 ~ 8 回の講義で、提起した質問に対するすべての回答が非常に詳細に (そして非常に雄弁に) 説明されます。 .

于 2010-12-09T07:15:32.170 に答える
0

基本的に、C は引数をスタックにプッシュして渡します。ポインター型の場合、ポインターはスタックにプッシュされます。

C についての 1 つのことは、呼び出された関数ではなく、呼び出し元がスタックを復元することです。このように、引数の数は変化する可能性があり、呼び出される関数は渡される引数の数を事前に知る必要はありません。

戻り値は、AX レジスタまたはそのバリエーションで返されます。

于 2010-12-09T07:17:38.040 に答える