61302


私はコンピュータについて勉強中で、スタックが低いアドレスに向かって積まれることを学びました。  
そして、実験的にこのようなコードを書きました

[code]

    alignas(long) int k = 0xcafecafe;
    k++;
    uint8_t buf[] = {0,1,2,3,4,5,6,7};
    alignas(16) uint8_t x,y,z;
    x = 1;
    y = 2;
    z = 3;
    
[/code]

lldbでメモリを見てみると、確かにx,y,zは上にむかって積まれていましたが、0xcafecafe+1は配列の`0x7ffeefbff880: 0x00
0x01 0x02 0x03 0x04 0x05 0x06 0x07`より上に積まれています。  
これはなぜですか？

[code]

    (lldb) x/128xb &z
    0x7ffeefbff840: 0x03 0xf8 0xbf 0xef 0xfe 0x7f 0x00 0x00
    0x7ffeefbff848: 0xa0 0xf8 0xbf 0xef 0xfe 0x7f 0x00 0x00
    0x7ffeefbff850: 0x02 0x00 0x00 0x00 0x01 0x00 0x00 0x00
    0x7ffeefbff858: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
    0x7ffeefbff860: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
    0x7ffeefbff868: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
    0x7ffeefbff870: 0xff 0xca 0xfe 0xca 0x00 0x00 0x00 0x00
    0x7ffeefbff878: 0x00 0x00 0xff 0xff 0x00 0x00 0x00 0x00
    0x7ffeefbff880: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07
    0x7ffeefbff888: 0xc8 0x00 0x9a 0x1c 0xb6 0x13 0x7a 0x6c
    0x7ffeefbff890: 0xa8 0xf8 0xbf 0xef 0xfe 0x7f 0x00 0x00
    0x7ffeefbff898: 0xe5 0x82 0xec 0x69 0xff 0x7f 0x00 0x00
    0x7ffeefbff8a0: 0xe5 0x82 0xec 0x69 0xff 0x7f 0x00 0x00
    0x7ffeefbff8a8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
    0x7ffeefbff8b0: 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00
    0x7ffeefbff8b8: 0x78 0xfa 0xbf 0xef 0xfe 0x7f 0x00 0x00
    
[/code]


CC BY-SA 4.0
2019-12-10T10:03:36.530
61298
2019-12-12T01:26:52.030
37013
question
c++ c debugging
スタックが低いアドレスに向かって積まれない場合はありますか？


コードが断片的過ぎて、提示コードが自動変数なのか静的変数なのかすらわからないんだけど、読者のほうで再現できる程度のソースコードを提示してもらえると幸い。

で、提示コードが真に自動変数に展開されるコードであるとして：

> これはなぜですか？

自動変数が宣言順にスタックに配置される保証は何一つない、が回答。 [c](/questions/tagged/c "'c' のタグが付いた質問を表示") /
[c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
コンパイラは（自動）変数をメモリ上どんな配置をしても構わないので。

スタックのグロウ方向を知りたいなら GNU autoconf の `alloca()` 用チェックみたいにいろいろ工夫が必要そうっす。


CC BY-SA 4.0
2019-12-10T11:23:14.257
61301
2019-12-10T11:23:14.257
8589
61298
answer


関数内の自動変数の配置順番なんかは、コンパイラの自由裁量の範囲で、どうにでも変わるものでしょう。

例えばこんな記事があります。  
[第八回-03
メインメモリとアドレス](https://brain.cc.kogakuin.ac.jp/~kanamaru/lecture/prog1/08-03.html)

> いずれにせよ、変数をメモリ上にどう配置するかはコンパイラの仕事なので、任せておけば問題はない。  
>  しかし、変数 (今回学んだのは自動変数と呼ばれるもの) がメモリに配置されるイメージを掴むことは大変重要である。

続きでこれとか。  
[第九回-01
スタック領域上での配列の配置](https://brain.cc.kogakuin.ac.jp/~kanamaru/lecture/prog1/09-01.html)  
[第九回-02
関数内の変数のメモリ配置](https://brain.cc.kogakuin.ac.jp/~kanamaru/lecture/prog1/09-02.html)

こちらはスタック内とは書いていないので固定のデータ領域かもしれませんが。  
[変数がメモリ上でどのように配置されているかメモ](https://qiita.com/miz_erz/items/fdf3957549945746cb1b)

デバッグ版かリリース版か、最適化の有無および最適化もサイズ優先か速度優先か、など様々な条件により変数のレイアウトは変わるでしょう。

**「スタックが低いアドレスに向かって積まれることを学びました。」** に関しては、こちらの概念の方が相応しいですね。  
[C言語 スタックメモリ【ローカル変数が確保される仕組みを解説】](https://monozukuri-c.com/langc-stack-
memory/)

説明の主題とは微妙に違いますが、関数の中から更に関数を呼び出していくと、スタックの低いアドレスに向かって消費量が伸びていきます。

ただし、関数を呼び出すときのパラメータの積み方は、OSのAPIの呼び出しとか、プログラミング言語を混合して使う等のために決まっています。  
それでも、いくつかのパターンがあって、どれが使われているか、どれを使うかは、選択する必要があります。


CC BY-SA 4.0
2019-12-10T11:28:15.463
61302
2019-12-10T11:36:29.880
2019-12-10T11:36:29.880
26370
26370
61298
answer


あとスタックが低いアドレスに進むのは、そういう命令仕様の CPU を使っているからであって、スタックが高いアドレスに進む仕様の CPU
もあるです。ユーザーの多い x86 では、いわゆる「スタックに積む命令」例えば `CALL` だとか `PUSH`
だとかでスタックポインタが減算される仕様なので、みんなそれに慣れちゃっているからスタックは低アドレスへ進むもんだと信じ切っています。ですが PA-RISC
などではスタックは高いアドレスに進む仕様です。

[code]

    #include <stdio.h>
    void growtester2(int* oldarg, int* newarg) {
        if (oldarg < newarg) printf("stack grows + direction\n");
        else if (oldarg > newarg) printf("stack grows - direction\n");
        else printf("stack grow direction is unknown\n");
    }
    
    void growtester1(int* arg) {
        int tester=0;
        growtester2(arg, &tester);
    }
    
    int main() {
        int tester=0;
        growtester1(&tester);
    }
    
[/code]

同一配列内要素を指さないポインタの大小比較は未定義 ([c](/questions/tagged/c "'c' のタグが付いた質問を表示")) 未規定
([c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示"))
なのですが、そこを敢えて比較することでスタックの進行方向を知ることができます。

cygwin x86 や x64 の場合

[code]

    $ gcc -O0 -g stkgrow.c
    $ ./a.exe
    stack grows - direction
    $
    
[/code]

`hppa2.0w-hp-hpux11.11` の場合

[code]

    $ gcc -O0 -g stkgrow.c
    $ ./a.out
    stack grows + direction
    $
    
[/code]

このソースコードでは単純すぎて、最適化するとコンパイラが全面的にインライン展開を行ってしまうので所望の動作にならないことに注意。 GNU autoconf
のスタック方向調査コードは末尾再帰にならない再帰関数を作ってインライン展開されないような工夫をしています。興味がある読者は調査してみると吉。


CC BY-SA 4.0
2019-12-12T01:26:52.030
61356
2019-12-12T01:26:52.030
8589
61298
answer