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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • 私のこれまでの考えではconstは不揮発性ROMに持つものと考えていました、スタック領域ということもあるのですか、初耳です、そうなるとconstで定数扱いした場合に関数コールするたびに値が違いはしないのか?ということですが関数の最初で、定数をスタック領域にコピーしているなら確かにconstと宣言したのと同じ動作はしますね、constとは必ずしもROMに持つとは限らないということですね、なるほどです、動作が正しければいいわけで特別な規則はないわけですね、ただ毎回最初で、定数をスタック領域にコピーしているならCPU時間が無駄ということには気がつかないんでしょうかね、関数の外でグローバルで宣言するのもありますが、見通しが悪くなりますし、いっそのこと#pragmaでも使用して強制的に割り当てるとか、コンパイラの最適化を最高にしてみてはどうですか、プログラムのコードをデータとして扱えないような制約でもあるのですかね、変数宣言するのはやめにして規定値をコード中で指定するようなやりかたにしてみることは可能でしょうかね、毎回最初で、定数をスタック領域にコピーしているのなら、後で書き換えがきくということですかねそうなるとconstにはならないような、const宣言したにもかかわらずstaticになってる

  • 不確かな経験と遠い昔に読んだK&Rのプログラミング言語Cの記憶です。constは型修飾子で、そこで宣言されるのは、定数ではなく値の変化しない変数と認識してます。K&Rのプログラミング言語CではPASCALのような定数定義は無く、defineで大文字で記述するように決めていたような記憶があります。

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

     私の言いたいことは、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にコピーした方が速いです。

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

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

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

    -------

    まず、言語的に、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にコピーした方が速いです。

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

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

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

  • リカルドです。

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

    ① 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に代入しようとしたとき、コンパイラエラーがちゃんと出るのかそっちのほうが気になります。

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

    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 なので値の変更ができないだけです。