90979
私は今C言語のポインタについて学習していて、本に書いてある内容と私の環境で試した内容に齟齬があり、質問しました。どうか皆様の知恵をお貸しください。

### 本に書いてあった内容

関数が呼び出されると、関数のスタックフレームがスタックにプッシュされる。  
スタックフレームが作られるとき、まず関数の引数が後ろから順にプッシュされ、次にリターンアドレス、局所変数と続く。局所変数は宣言の順番と逆順にプッシュされる。

例）

```

        float average(int *arr, int size){
            int sum;
            ...
        }
    
```

このような関数が呼び出されたときは、それぞれの変数のアドレスは以下のようになる。（intのサイズが4の場合）

```

    sum 480
    arr 500
    size 504
    
```

先にプッシュされた方がアドレスの値は大きくなる。  
アドレスは実行の都度変化するが、順番は通常は変化しない。

### 私が試したこと

スタックフレーム内の各変数の位置を調べるため以下のコードをコンパイル＆実行。

**コンパイル＆実行環境**

  * Ubuntu20.04(wsl2)

  * GCC 9.4.0
```      #include <stdio.h>

     #include <stdlib.h>
     int* a(int, int);
    
     int main(void){
         int* mem1 = (int*)malloc(sizeof(int));
         printf("seizeof(int) = %ld\n", sizeof(int));
         printf("mem1's memory address is %p\n", mem1);
         // free(mem1);
         int* c = a(10, 11);
     }
    
     int* a(int num1, int num2){
         int hoge = 0;
         int fuga = 1;
         int* mem2 = (int*)malloc(sizeof(int));
         printf("mem2's memory address is %p\n", mem2);
         printf("hoge's memory address is %p\n", &hoge);
         printf("fuga's memory address is %p\n", &fuga);
         printf("num1's memory address is %p\n", &num1);
         printf("num2's memory address is %p\n", &num2);
         return mem2;
     }
    
```

**実行結果**

```

        seizeof(int) = 4
        mem1's memory address is 0x5598cb3242a0
        mem2's memory address is 0x5598cb3246d0
        hoge's memory address is 0x7fff41e881b8
        fuga's memory address is 0x7fff41e881bc
        num1's memory address is 0x7fff41e881ac
        num2's memory address is 0x7fff41e881a8
    
```

試したコードでは、参考までに動的変数のメモリ割り当て位置も調べています（`mem1`と`mem2`）。必要なければ無視してください。  
本に書いてあった通りならば、アドレスの位置は  
アドレス **大**`num2 > num1 > fuga > hoge` **小**  
となるはずです。しかし、私の環境で試した結果は、  
アドレス **大**`fuga > hoge > num1 > num2` **小**  
です。  
以下のルール  
**1**. 局所変数は **宣言の順と逆に** プッシュされる  
**2**. 引数は **後ろから順に** プッシュされる  
**3**. 引数がプッシュされた **後に** 局所変数がプッシュされる  
のうち、 **１** は満たしていますが **２** ， **３** には違反してしまっています。

他の環境でも試したところ、  
VisualStudio `num2 > num1 > hoge > fuga` ( **１** ×， **２** 〇， **３** 〇)  
Wandbox(gcc 12.1.0) `hoge > fuga > num1 > num2` ( **１** ×， **２** ×， **３** ×)  
Wandbox(clang 14.0.0) `num1 > num2 > hoge > fuga` ( **１** ×， **２** ×， **３** 〇)  
と、見事にバラバラです・・・  
これは、コンパイラや、OSやハードウェアなどの実行環境が違えばメモリ割り当てのルールも違うだろうということでいいのでしょうか？本やネットの文献で調べたところでは、
**１，２，３** のルールが環境に依存するという情報は見つけられませんでした。私の調べが甘いだけでしょうか？  
こんな細かいところなんてどうでもいいじゃないかと思われるかもしれませんが、性分で気になってしまうのです。すみません。

この現象についてどなたかご教授いただけますと幸いです。  
よろしくお願いします。
CC BY-SA 4.0
2022-09-07T07:35:08.247
90978
2022-09-07T08:43:09.343
2022-09-07T07:38:08.423
3060
54289
question
c ポインタ メモリ管理
関数スタックフレーム内でのメモリの割り当て位置がおかしい？
結局のところこの辺を規定しているのは ABI (Application Binary Interface) と呼ばれる文書です。有名どころだと x86
ABI とか x64 ABI とか arm eabi とか。この規定は CPU の仕様によって異なり、また OS 自体の採択した仕様によっても異なります。

> 先にプッシュされた方がアドレスの値は大きくなる。

などは [CPU の仕様によって異なります](https://ja.stackoverflow.com/questions/61298/) 。

> 関数の引数が後ろから順にプッシュされ

などは OS が定めた仕様によって異なります ( [__cdecl](https://docs.microsoft.com/ja-
jp/cpp/cpp/cdecl) / [__stdcall](https://docs.microsoft.com/ja-
jp/cpp/cpp/stdcall) )

> 局所変数は宣言の順番と逆順にプッシュされる。

これはコンパイラの[最適化の結果で変わります](https://ja.stackoverflow.com/questions/61298/) 。

ということで本で文字化することができるのは特定の一例のみなので CPU
やコンパイラなどが進化し続けている現代においては、文字化した時点ですでに時代遅れになっていることがあります。細かいところが気になるのはプログラマとしてとても良い資質だと思いますが、細かすぎて今この瞬間に知っても仕方ないところは適当に流せるともっといいですね。
CC BY-SA 4.0
2022-09-07T08:12:20.047
90979
2022-09-07T08:12:20.047
8589
90978
answer
スタックについては、コンパイラを作成するなどの場合にはおおいに勉強する必要があります。  
また、組み込み系のコードによっては詳しいルールを知っている必要があります。

ただし、一般のアプリケーション等を作成するのであれば、  
1\. スタックの使い方はコンパイラ任せです(プログラマはあまり気にしません)。  
2\. スイッチによる引数のスタック順位を変えられる場合もあります(関数が呼べない場合もあります)。  
3\. もちろんCPUにも依存します(レジスタ等の仕組みが異なるので仕方ありません)。  
4\. 最適化オプションによっても異なる場合があります(複数のモジュールでそろえる必要があるかもしれません)。

などについて、(うっすらとでも)知っている必要があると思います。  
一般のアプリケーションを作成する場合に必要な知識としては、コンパイラとスタック関連のオプションによっては、正しく動作しない場合があるということなどを記憶しておきましょう。
CC BY-SA 4.0
2022-09-07T08:43:09.343
90981
2022-09-07T08:43:09.343
3793
90978
answer