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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

2896閲覧

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

退会済みユーザー

退会済みユーザー

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2018/12/04 14:11

編集2018/12/04 16:23

はじめに

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

やっていること

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

c

1#include <stdio.h> 2 3void some_func(int i, int a, int b) 4{ 5 printf("some_func%d..&a: %p\n", i, &a); 6 printf("some_func%d..&b: %p\n", i, &b); 7 if (i > 3) { 8 printf("end\n"); 9 } else { 10 some_func(i+1, a, b); 11 } 12} 13 14int main() { 15 int a, b; 16 some_func(0, a, b); 17 return 0; 18}

期待した結果

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

実際の結果

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

terminal

1$./sample 2some_func0..&a: 0x7ffee9a590f8 3some_func0..&b: 0x7ffee9a590f4 4some_func1..&a: 0x7ffee9a590c8 5some_func1..&b: 0x7ffee9a590c4 6some_func2..&a: 0x7ffee9a59098 7some_func2..&b: 0x7ffee9a59094 8some_func3..&a: 0x7ffee9a59068 9some_func3..&b: 0x7ffee9a59064 10some_func4..&a: 0x7ffee9a59038 11some_func4..&b: 0x7ffee9a59034 12end

試したこと・考えたこと

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

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

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

gcc

1$gcc -v 2Configured 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 3Apple LLVM version 10.0.0 (clang-1000.11.45.5) 4Target: x86_64-apple-darwin18.2.0 5Thread model: posix 6InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

clang

1Apple LLVM version 10.0.0 (clang-1000.11.45.5) 2Target: x86_64-apple-darwin18.2.0 3Thread model: posix 4InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

追記

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

C

1#include <stdio.h> 2 3void __cdecl some_func( 4 int a, int b, int c, int d, int e, int f, int g, int h, int i, int j 5) {} // do nothing 6 7int main() { 8 some_func(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 9 return 0; 10}

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

sample.s

1_some_func: 2 . 3 . 4 . 5 movl 40(%rbp), %eax 6 movl 32(%rbp), %r10d 7 movl 24(%rbp), %r11d 8 movl 16(%rbp), %ebx 9 movl %edi, -12(%rbp) 10 movl %esi, -16(%rbp) 11 movl %edx, -20(%rbp) 12 movl %ecx, -24(%rbp) 13 movl %r8d, -28(%rbp) 14 movl %r9d, -32(%rbp) 15 movl %ebx, -36(%rbp) ## 4-byte Spill 16 movl %r10d, -40(%rbp) ## 4-byte Spill 17 movl %r11d, -44(%rbp) ## 4-byte Spill 18 movl %eax, -48(%rbp) ## 4-byte Spill 19 . 20 . 21 . 22_main: 23 . 24 . 25 . 26 movl $1, %edi 27 movl $2, %esi 28 movl $3, %edx 29 movl $4, %ecx 30 movl $5, %r8d 31 movl $6, %r9d 32 movl $7, %eax 33 movl $8, %r10d 34 movl $9, %r11d 35 movl $10, %ebx 36 movl $0, -12(%rbp) 37 movl $7, (%rsp) 38 movl $8, 8(%rsp) 39 movl $9, 16(%rsp) 40 movl $10, 24(%rsp) 41 movl %ebx, -16(%rbp) ## 4-byte Spill 42 movl %r11d, -20(%rbp) ## 4-byte Spill 43 movl %r10d, -24(%rbp) ## 4-byte Spill 44 movl %eax, -28(%rbp) ## 4-byte Spill 45 callq _some_func

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

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

投稿2018/12/04 14:54

daisuke7

総合スコア1563

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2018/12/04 15:46

そうか、引数の数を増やすと良いですね。
退会済みユーザー

退会済みユーザー

2018/12/04 17:05

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

2018/12/04 17: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 00:32

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

0

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

投稿2018/12/04 14:18

y_waiwai

総合スコア87747

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/12/04 16:58

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

0

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

投稿2018/12/04 14:48

otn

総合スコア84499

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問