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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    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扱いで定数伝播させるでしょうし。

Reply
  • fujitaさん

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

    IKUZOさん

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

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

    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の目的は定数の定義ではないと思います。

  • そうですよね、BufferSize等は#defineの方が一般的ですよね、constしたい場面等はconst char *st[]={"JP","US"};等の時ですね、ただPCの場合や多くの場合ROM、RAMにこだわっても意味がないですね、実行コード領域がRAMですから、P領域は保護がかけてあるだけです

  • 皆さんC言語だったんですね。C言語なら#defineでいいんでないでしょうか。(他に無いので)
    無理やり理由を挙げれば、#defineはスコープが限定できないですね。名前汚染でぶつかったりしません?

    最初の方に「オブジェクト」とあったので反射的にC++で書いてしまっていました。そのあたりは加味して頂けると助かります (^^;
    C++はconstは言語的に定数でいいと思ってます。CC-RXを使う場合は、クラス内定数が使えないので、整数なら enumハック 使ってます。スコープはとにかく限定したいので。

    >IKUZOさん
    今回の話で文字列にも#define使えば、 コピーのオーバーヘッドなくなる可能性があると思いました。
    C言語なら使っていこうと思ってます。

  • pcook さん#defineは特に規定とか一般的ということについては知らないんですが、原始的Cを使用してきた私にとって

    const int b=3;

    abs=b*2-5;

    cc=x+y+b;

    というような場面ではconstじゃなくて#define使用していますよ(私的)ということでして、

    #define使用するとスコープがということですが適当に逃げてます例えば#define  関数名_変数名_変数型 subb_xp_int とか

    それに#defineが2重定義なら警告も出せるようで、あまり心配していません、enumもおっしゃるように便利なので最近使ってます

  • const を使う所は、配列ではないでしょうか。

    私の実験しているのは、メッセージのような文字列です。

    数値だとしても、配列番号を指定して呼び出す変数じゃないでしょうか。

  • 私もconstの使いどころは変更不可の変数です。専ら関数配列などに使用します。

    immediateな固定値はdefineを用いることが多いですね。