1. ホーム
  2. c

[解決済み] スタックは上に伸びるのか、下に伸びるのか?

2023-02-22 08:19:09

質問

C言語でこのようなコードがあります。

int q = 10;
int s = 5;
int a[3];

printf("Address of a: %d\n",    (int)a);
printf("Address of a[1]: %d\n", (int)&a[1]);
printf("Address of a[2]: %d\n", (int)&a[2]);
printf("Address of q: %d\n",    (int)&q);
printf("Address of s: %d\n",    (int)&s);

と出力されます。

Address of a: 2293584
Address of a[1]: 2293588
Address of a[2]: 2293592
Address of q: 2293612
Address of s: 2293608

ということで、なるほど a から a[2] というように、メモリアドレスが4バイトずつ増えていきます。 しかし q から s となり、メモリアドレスが4バイト分減少します。

2つ疑問があります。

  1. スタックは上に伸びるのか、下に伸びるのか?(この場合、私には両方に見えます。)
  2. の間に何が起こりますか? a[2]q というメモリアドレスがあります。なぜそこに大きなメモリの違いがあるのでしょうか?(20バイト)です。

注:これは宿題のような質問ではありません。私はスタックがどのように動作するかについて興味があります。どんな助けでもありがとうございます。

どのように解決するのですか?

スタックの動作 (グロウアップまたはグロウダウン) は、アプリケーション バイナリ インターフェイス (ABI) とコール スタック (別名アクティベーション レコード) がどのように構成されているかに依存します。

プログラムはそのライフタイムを通じて、OS のような他のプログラムと通信するよう束縛されます。ABI は、プログラムが他のプログラムとどのように通信できるかを決定します。

異なるアーキテクチャのスタックはどちらにも成長できますが、あるアーキテクチャでは一貫しています。以下を参照してください。 この wiki のリンクを参照してください。しかし、スタックの成長は、そのアーキテクチャのABIによって決まります。

例えば、MIPSのABIを例にとると、コールスタックは以下のように定義されています。

関数'fn1'が'fn2'を呼び出すことを考えてみましょう。ここで、'fn2'から見たスタックフレームは次のようになります。

direction of     |                                 |
  growth of      +---------------------------------+ 
   stack         | Parameters passed by fn1(caller)|
from higher addr.|                                 |
to lower addr.   | Direction of growth is opposite |
      |          |   to direction of stack growth  |
      |          +---------------------------------+ <-- SP on entry to fn2
      |          | Return address from fn2(callee) | 
      V          +---------------------------------+ 
                 | Callee saved registers being    | 
                 |   used in the callee function   | 
                 +---------------------------------+
                 | Local variables of fn2          |
                 |(Direction of growth of frame is |
                 | same as direction of growth of  |
                 |            stack)               |
                 +---------------------------------+ 
                 | Arguments to functions called   |
                 | by fn2                          |
                 +---------------------------------+ <- Current SP after stack 
                                                        frame is allocated

これでスタックが下に伸びていくのがわかると思います。つまり、変数が関数のローカルフレームに割り当てられると、変数のアドレスは実際に下に伸びていくのです。コンパイラはメモリ割り当ての際の変数の順番を決めることができます。(あなたの場合、最初にスタックメモリを割り当てるのは 'q' か 's' のどちらかです。しかし、一般的にコンパイラは変数の宣言の順番通りにスタックメモリを割り当てる)。

しかし、配列の場合、割り当てにはシングルポインタしかなく、割り当てられる必要があるメモリは実際にはシングルポインタによって指されます。配列の場合、メモリは連続である必要があります。そのため、スタックは下に伸びていきますが、配列の場合は上に伸びていきます。