定数が何故スタック領域に配置されるのか

HEWとRX621で、コンパイルしています。

ローカル変数を「const」にすると、スタック領域に配置されます。

関数の最初で、定数をスタック領域にコピーするプログラムが書かれています。

「const static」にすれば、定数のセクションに配置されます。

「const」は定数で書き込めないんだから、スタック領域に書き込むのはおかしいと思うのですが。

Parents
  • 関数内のオブジェクトに static を指定しないと暗黙的に auto ということになるので自動記憶域期間を持ちます。そのブロック内でのみの寿命を持つのでオブジェクトがスタック上に配置されるのは正しいです。

  • RAMじゃなくてROMでいいじゃんってお話ですよね。

    仰る通り、ROMでいいと思いますし、場合によっては、定数領域でもなく即値でいいと思います。

    const有無とstatic有無でどこに配置されるか、

    コンパイラ/リンカのマニュアルに書いてあり、

    コード書く人がコントロールできますよってことだと、私は捉えております。

    といって、何の役にたつのか私にはイマイチ分からなかったりします。

    RAMとROMでアクセスウェイト時間が違ったりする場合に、高速化のために配置制御したくらいです。

    逆に、コンパイラにガンガン最適化して欲しい場合は、どう書けばいいんでしょうか (^^;

  •  みなさん、書き込み有難う御座います。

     私の言いたいことは、IKUZOさんの書かれている通りで、スタックにコピーするだけ無駄だなと言う事です。

     コピー元を使えば、メモリーも時間も節約できます。定数と書いたけど、実際の試験では文字列の事です。

    メッセージだと固定データで変更しないですから、「const」を上手に使おうと言う事で実験してみました。

  • 誤解を招くような書き方ですいません。

    局所変数のお話に関してですが、

    -------

    まず、言語的に、const変数は、非constで初期化する書き方ができますので、

    const int a = 1;

    const int cached_result = Func(x);

    はちょっと意味が違うと思います。後者も定数なんですが、初期化は動的で、書き換え禁止のような意味でしかないです。

    後者は、あえて確保されるならRAMになりますよね。

    -----

    次に最適化の話で、よく使いまわす定数は、余っているレジスタに突っ込んで、使いまわされるのではないでしょうか。これは、RAMにもROMにも置かれないですよね。(というか、私はレジスタはRAMと捉える傾向なので、ほとんどの計算はRAM同士になっていると解釈します)

    ビットや1バイトの定数なんかになると、即値で命令に埋め込まれてしまうのではないでしょうか。

    -----

    コピーのオーバーヘッドについても、ROMアクセスにオーバーヘッドがあると有利不利は微妙ですね。

    スレ主さんはRXなので、話がずれてしまいますが、RL78はRAMノーウェイト、ROMが3クロックくらいでしたっけ。

    例えば、sin(x)の計算を高速化するために、結果のテーブルを作ったとします。よく使う場合は、RAMにコピーした方が速いです。

    すいません、ここまでいくと、もう局所変数の話でもないですね。

  • レスが入れ違いになってしまいました。主題からずれてすいません。

    最適化して無駄なことしてたら、コンパイラがダメ、ってことでいいです (^^;

  • リカルドです。

    変数が何処に配置されるか調べた結果を報告します。 

    ① const static char[] = "A" : C-1セクション

    ② static const char[] = "B" : C-1セクション

    ③ const           char[] = "C" : スタックエリア

    ④                    char[] = "D" : スタックエリア

    ⑤           static char[] = "E" : R-1、D-1セクション(初期値付き変数)

    予約語の前後を入れ替えた、①と②は同じ。

    ③と④は、同じ。関数の頭で、定数をスタックにコピーする命令が入る。

    ③が①と同じになると思ったが、④と同じになった。

    ⑤は main() 関数を実行する前に、DセクションからRセクションにコピーしなければならない。

    ③は、次のようになりました。SMOVFで、複数データをコピーしています

    MOV.L       #L14,R2

    MOV.L       R0,R1

    MOV.L       #0000000AH,R3

    SMOVF

  • リカルドさん、おもしろい情報ありがとうございます。

    私としては、

    const char* a = "A";
    const char* const b = "B";

    の書き方で、aは非const変数、bはconst変数と思っていますが、
    書いて頂いた内容は、全て前者のものでしょうか。
    そうすると全てRAMになるかと思ったのですが、そうでもないんですねぇ。

    ポインタ(配列)の代わりに、intあたりで試すといいかなと思いました。
    あと、最適化すると後ろのコードによって変わるかってところですね。

    自分でも試したいのですが、しばらく出先です。すいません。

  • 間違いがあったので、追記削除しました。

    const char a[] = "A";
    の宣言だと、aには代入できないですね。
    私の勘違いでした。すいません。
    (const char* const a = "A";  と同じ扱いですかね)

  • > const char a[] = "A";
    > の宣言だと、aには代入できないですね。

    代入でなく初期値なので問題ありません。

  •  4っつ前の書き込みは、分かり易いように編集してあります。

    試験したときの文字列はもっと長いので、転送バイト数のカウンタ値に食い違いが出ます。

    要点は、このようなコピー命令が入っている事の証明です。

  • fujitaさん

    > 代入でなく初期値なので問題ありません。
    はい、これも私の書き方が悪かったですが、
    const char a[] = "A"; の書き方では、aはconst変数です(初期化が必要)。
    この書き方を、const char* a = "A"; と同じだと勘違いしていたという意味です。
    この場合は、非constで、初期化はなくてもよいです。
    const char* const a = "A"; はconst変数で初期化が必要ですね(=最初の書き方)

    リカルドさん
    要点は理解しています。
    結局、ローカル変数については、最適化の問題かなと思ってまして、リカルドさんの実験結果は面白かったです。
    最適化されれば使っていない変数は削除されたりするわけで、C言語の宣言と配置は一対一にならないのかな、と(今なったとしても、コンパイラのバージョンで変わるとか)。
    あまり時間使えてませんが、マニュアルになにか保証される記述が無いかなーと思って、見てるんですけど、見つけられないです。

  • constなauto変数って考えたことがなかったですが、この結果は新鮮ですね。

    でもconst修飾子は定数という意味だけだし、static修飾子は静的という意味だけ。

    定数=ROMという考えは我々の経験則であって、コンパイラの定義ではないような気もします。

    関数内でstatic無し(自動変数、動的配置)と言っている時点でstackに積まれるのは理に適っているかな?とも思います。

    どちらかと言うと、auto変数のconstに代入しようとしたとき、コンパイラエラーがちゃんと出るのかそっちのほうが気になります。

Reply
  • constなauto変数って考えたことがなかったですが、この結果は新鮮ですね。

    でもconst修飾子は定数という意味だけだし、static修飾子は静的という意味だけ。

    定数=ROMという考えは我々の経験則であって、コンパイラの定義ではないような気もします。

    関数内でstatic無し(自動変数、動的配置)と言っている時点でstackに積まれるのは理に適っているかな?とも思います。

    どちらかと言うと、auto変数のconstに代入しようとしたとき、コンパイラエラーがちゃんと出るのかそっちのほうが気になります。

Children
  • いろいろ気になってます (^^;

    autoなconstへの代入は、エラーになりますよ。言語仕様ですね。
    constつけれるところは、つけて得することはあっても、損することはないはずです。

    「定数」っていう言葉については、先日書いた通り、
    const int a = 1;

    const int cached_result = Func(x);
    は意味が違いますよね。

    前者は常に定値ですが、後者は編集不可のキャッシュ値みたいな意味で、
    xの値が変われば、その行を通った時のcached_resultの値は変わります。

    コンパイラにとっては、ローカル変数の場合、
    const int a = 1;

    static const int a = 1;
    は上記の前者の意味ならまったく同じですよね(ガンガン最適化されるでしょう)。

  • > const char a[] = "A"; の書き方では、aはconst変数です(初期化が必要)。

    aは配列名であり変数ではありません。要素数を省略しなければ初期化も必須ではありません。

    > const char* const a = "A"; はconst変数で初期化が必要ですね(=最初の書き方)

    この場合も初期化は必須ではありません。const なので値の変更ができないだけです。

  • > コンパイラにとっては、ローカル変数の場合、

    > const int a = 1;

    > と

    > static const int a = 1;

    > は上記の前者の意味ならまったく同じですよね(ガンガン最適化されるでしょう)。

    auto 変数は個別のアドレスを持つので違います。

  • 例えば以下のプログラムで hoge(0) と piyo(0) の結果は異なります。

    #include <stdio.h>
    
    const int* hoge(int b)
    {
        const int a = 1;
        if (b == 1 || &a == hoge(1)) {
            return &a;
        } else {
            return NULL;
        }
    }
    
    const int* piyo(int b)
    {
        static const int a = 1;
        if (b == 1 || &a == piyo(1)) {
            return &a;
        } else {
            return NULL;
        }
    }
    
    int main(void)
    {
        printf("hoge(0) = %p\n", hoge(0));
        printf("piyo(0) = %p\n", piyo(0));
        return 0;
    }
    
  • 付き合っていただいて、ありがとうございます

    >> const char a[] = "A"; の書き方では、aはconst変数です(初期化が必要)。
    >
    > aは配列名であり変数ではありません。要素数を省略しなければ初期化も必須ではありません。
    >
    >> const char* const a = "A"; はconst変数で初期化が必要ですね(=最初の書き方)
    >
    > この場合も初期化は必須ではありません。const なので値の変更ができないだけです。

    私の勉強不足で、配列名とconst変数の違いがわかりませんが、
    CC-RX最新バージョンで、

    const char a[2];

    はエラーE0520257です。

    const char* const a;

    はエラーE0520257です。

    >> コンパイラにとっては、ローカル変数の場合、
    >> const int a = 1;
    >> と
    >> static const int a = 1;
    >> は上記の前者の意味ならまったく同じですよね(ガンガン最適化されるでしょう)。
    >
    > auto 変数は個別のアドレスを持つので違います。

    なるほど、これはよい覚え方ですね。勉強になりました。
    hoge()の内容は、テンポラリのアドレス返すので、使えないですが、反例としては理解しました。
    アドレスで外のスコープへ渡せることを考えずに、同じだと表現していました。
    piyo()の内容は、同じデータが2回配置されないように、文字列なんかでは私も使います。
    (環境によっては、定数の重複はビルド時にひとつにしてくれる機能があったりしますが、変更時に1個所さわるだけにしたいので)

  • > CC-RX最新バージョンで、
    >
    > const char a[2];
    >
    > はエラーE0520257です。
    >
    > const char* const a;
    >
    > はエラーE0520257です。

    CC-RX V2.04.01 にて下記のコードをコンパイルしたところ

    void hogera(const char* a)
    {
        (void)a;
    }
    
    void hoge(void)
    {
        const char a[2];
        hogera(a);
    }
    
    void piyo(void)
    {
        const char* const a;
        hogera(a);
    }
    
    void main(void)
    {
        hoge();
        piyo();
    }
    

    警告は出ますがビルドは成功しました。ひょっとして C++ でコンパイルされてるとかではないですか? C と C++ では const の機能は全く異なります。

    ========== 全ビルドの開始(2016年4月18日 13:27:48) ==========
    ------ ビルド開始(test, DefaultBuild) ------
    >test.c
    W0511179:The evaluation version is valid for the remaining 13 days.
    test.c(14):W0520257:Const variable "a" requires an initializer
    >DefaultBuild\test.abs DefaultBuild\test.mot
    The evaluation period has expired.
    Renesas Optimizing Linker Completed
    ------ ビルド終了(エラー:0個, 警告:2個)(test, DefaultBuild) ------
    ========== 終了しました(成功:1プロジェクト, 失敗:0プロジェクト)(2016年4月18日 13:27:53) ==========
    
  • 最初の方の話に戻してしまいますが、昔からローカル変数(auto属性)で文字配列を宣言して初期化すると、スタックに確保されコピーが発生するので、static宣言するというテクニックがあったと思います。

    それにconstが付くと、その後の変更でコンパイルエラーが出るというだけの話ではないでしょうか。

    頭が良くて気の利いたコンパイラなら、最適化時に思いもよらない事はやってくれますが...

  • const int a=2;

    b +=a;

    よりも b +=2;の方が速いと思うしわかりやすいですね、たぶん adda.l er0,#2 とかにコンパイルされるんですよね、もっと早くしようとしたら

    register int a=2; これなんかどれぐらい早いんでしょうか?

  • fujitaさん

    はい、C++です。通りで噛み合わないわけで。。今回のは途中で気づけた内容でした。。
    不定で変数を使うとか、恐れ多いことはしないので、Cのconst仕様忘れてました。

    IKUZOさん

    そこまで期待して書いてますね。メモリ確保されるなんて思ってません (^^;
    最適化だけで言えば、ローカル変数で再代入されていなければ、
    const付いていなくても、コンパイラはconst扱いで定数伝播させるでしょうし。

  • プログラムにおいて定数は分かり易さのためだと思います。

    char Buffer[10];

    main()

    {

      int i;

      for(i=0;i<10;i++){

         Buffer[i]=' ';

      }

    }

    よりも

    #define BufferSize 10

    char Buffer[BufferSize];

    main()

    {

      int i;

      for(i=0;i<BufferSize;i++){

         Buffer[i]=' ';

      }

    }

    の方が、BufferSizeに意味を持たせることができて、検証も修正も安全に行えます。定数の概念はこれになると思います。また、この場合には、

    const int BufferSize = 10;

    にする価値は無いと思います。もともと、定数の定義はコードの分かりやすさのためにあると思います。

    他の言語とは異なりC言語ではポインタを使用するプログラマが多い事やファンクションのみにしたことでconstが必要になってしまったのだと思います。constの目的は定数の定義ではないと思います。