質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.58%

c言語 仮引数においてスタックにしまう順番について

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 882

k_hmd

score 13

 はじめに

c言語 ポインタ完全制覇(2001年出版version)を読んでいて,本と違った動きをしたので,理由が知りたいです.

 やっていること

以下のコードは関数を呼び出し,仮引数がスタックにしまわれる順番を見ています.
また,スタックに積まれる様子を見たかったので,3回だけ再帰にしています.

#include <stdio.h>

void some_func(int i, int a, int b)
{
    printf("some_func%d..&a: %p\n", i, &a);
    printf("some_func%d..&b: %p\n", i, &b);
    if (i > 3) {
        printf("end\n");
    } else {
        some_func(i+1, a, b);
    }
}

int main() {
    int a, b;
    some_func(0, a, b); 
    return 0;
}

 期待した結果

本では仮引数bがはじめにスタックにしまわれ,その後に仮引数aがスタックにしまわれると書いてあった.
すなわち,宣言の順番の逆順にスタックにしまわれる.

 実際の結果

以下に示すように,仮引数aからスタックにしまわれる.

$./sample
some_func0..&a: 0x7ffee9a590f8
some_func0..&b: 0x7ffee9a590f4
some_func1..&a: 0x7ffee9a590c8
some_func1..&b: 0x7ffee9a590c4
some_func2..&a: 0x7ffee9a59098
some_func2..&b: 0x7ffee9a59094
some_func3..&a: 0x7ffee9a59068
some_func3..&b: 0x7ffee9a59064
some_func4..&a: 0x7ffee9a59038
some_func4..&b: 0x7ffee9a59034
end

 試したこと・考えたこと

gccとclangを両方試しましたが,両方ともアドレスは仮引数aのほうが大きくなった.
コンパイラによってメモリへのしまい方が異なるのかもしれない?
中級以上の本を読むのが初めてなので,知らない知識がありそう.

 補足情報(バージョンなど)

試した環境は以下のとおりです.
MacOX Mojave 10.14.1

$gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.0 (clang-1000.11.45.5)
Target: x86_64-apple-darwin18.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
Apple LLVM version 10.0.0 (clang-1000.11.45.5)
Target: x86_64-apple-darwin18.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

 追記

コードをシンプルなものに変えて実行しました.

#include <stdio.h>

void __cdecl some_func(
    int a, int b, int c, int d, int e, int f, int g, int h, int i, int j
) {} // do nothing

int main() {
    some_func(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
    return 0;
}


アセンブリは以下のようになりました.(一部略)

_some_func: 
         .
         .
         .
    movl    40(%rbp), %eax
    movl    32(%rbp), %r10d
    movl    24(%rbp), %r11d
    movl    16(%rbp), %ebx
    movl    %edi, -12(%rbp)
    movl    %esi, -16(%rbp)
    movl    %edx, -20(%rbp)
    movl    %ecx, -24(%rbp)
    movl    %r8d, -28(%rbp)
    movl    %r9d, -32(%rbp)
    movl    %ebx, -36(%rbp)         ## 4-byte Spill
    movl    %r10d, -40(%rbp)        ## 4-byte Spill
    movl    %r11d, -44(%rbp)        ## 4-byte Spill
    movl    %eax, -48(%rbp)         ## 4-byte Spill
        .
        .
        .
_main:
        .
        .
        .
    movl    $1, %edi
    movl    $2, %esi
    movl    $3, %edx
    movl    $4, %ecx
    movl    $5, %r8d
    movl    $6, %r9d
    movl    $7, %eax
    movl    $8, %r10d
    movl    $9, %r11d
    movl    $10, %ebx
    movl    $0, -12(%rbp)
    movl    $7, (%rsp)
    movl    $8, 8(%rsp)
    movl    $9, 16(%rsp)
    movl    $10, 24(%rsp)
    movl    %ebx, -16(%rbp)         ## 4-byte Spill
    movl    %r11d, -20(%rbp)        ## 4-byte Spill
    movl    %r10d, -24(%rbp)        ## 4-byte Spill
    movl    %eax, -28(%rbp)         ## 4-byte Spill
    callq    _some_func


アセンブラについて詳しくないのですが,確かにすぐにスタックにしまっているわけではないみたいです.
これは結局最後の4つだけ右からスタックにしまっているということで大丈夫ですか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

x86_64(たいていのIntel64ビットUNIX系がこれ)の標準的な関数呼び出しでは、6番目の引数まではレジスタで渡してしまいます。
関数に入ってから必要に応じてスタックに格納するので、そのアドレスを表示しても原則通りにはならないと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/05 00:46

    そうか、引数の数を増やすと良いですね。

    キャンセル

  • 2018/12/05 02:05

    7番目の引数からなんかいろいろコピーしているのはそういうことなんでしょうか?

    キャンセル

  • 2018/12/05 02:22

    https://wiki.osdev.org/Calling_Conventions
    ↑のSystem V X86_64 ってのが相当します。
    6番目までは、rdi, rsi, rdx, rcx, r8, r9に入れちゃいます。
    (rdiとかは64bitレジスタなので、32bitならediとか)
    7番目以降はスタックで渡します。

    キャンセル

  • 2018/12/05 09:32

    回答ありがとうございます。
    レジスタを使ってからスタックを使うんですね。

    キャンセル

+2

そこらへんはコンパイラにより違います。
引数が出て来る順に押し込むものもあれば、逆のもあり、中にはレジスタに渡して呼び出す、なんてのもありますね。
「呼び出し規約」でぐぐれば、それぞれのコンパイラの流儀がわかります

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/12/05 01:58

    規約で決まっていて,それがいろいろあるとは知りませんでした.orz
    ありがとうございます.

    キャンセル

+1

gccだと、アセンブラソースを出力させると分かりますが、-O0で最適化を抑止しても、引数はスタックでなくレジスタで渡しています。
レジスタに入れる順番は、ba0の順ですね。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.58%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る