質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.48%

  • C++

    3608questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

C++のconstについて

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 1,181

aoshanghai

score 18

C++のconstについてです。

以下の1つのヘッダと、2つのcppがあるとします。
そして、2つのcppは、以下のヘッダファイルを
それぞれ、別途インクルードしているとします。

----- test.h -----
const int g_Test = 0;
------------------

----- a.cpp -----
...
#include "test.h"
...
void FuncA()
{
  printf("g_Test = %d\n", g_Test);
}
...
-----------------

----- b.cpp -----
...
#include "test.h"
...
void FuncB()
{
  printf("g_Test = %d\n", g_Test);
}
...
-----------------

この場合、C++においては、
const int g_Test = 0;
のように、constがついているものについては
内部結合として、扱われるかと思います。

ここで疑問があるのですが、g_Testは、
a.cppとb.cppで、それぞれインクルードされて
利用されていますが、
内部結合なので、a.cppとb.cppのそれぞれの中で、
別々に、変数の領域が確保されるような気がしました。

しかし、ネット上で検索しても、明確な記述は
今のところ見つかっておりません。

試しに、手元のVisualStuidio2008でいろいろと
調べていたのですが、以下ようにマクロを利用した場合の
mapファイルを比較してみたところ、

----- test.h -----
#define g_Test (0)
------------------


mapファイルの中の.rdataや.bssが、const変数の場合も、
マクロの場合も同じでしたので、もしかしたら、
マクロと同等な扱いを受けているような気もしてきました。

ちなみに、constを外して

----- test.h -----
int g_Test = 0;
------------------


とすると、グローバル変数になるので、
ビルドエラーが出るようです。

ということで、
const int g_Test = 0;
と書かれたファイルを、それぞれ別のcppで
インクルードした場合について、
変数領域の確保がどうなっているか?、
マクロと同等のものとして扱われるか?、
といった点についてご存知でしたら、ご教示頂けないでしょうか?

お手数をおかけいたしますがどうぞよろしくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+1

こんにちは。

C++でconstつけたら、内部リンケージになることは知りませんでした。constだけでは外部リンケージなので、通常はstaticも付けないといけないと思ってました。
調べてみたら、内部結合と外部結合にて、C++はconstだけで内部リンケージになり、Cはstaticもつけないと内部リンケージにならないそうです。びっくり。

内部結合なので、a.cppとb.cppのそれぞれの中で、別々に、変数の領域が確保されるような気がしました。

文法上はその通りと思います。同じ値がコード領域に定義された場合、最適化して1つにしてしまうようなリンカもあるようです。(確認したことはないですが。)

マクロと同等のものとして扱われるか?、 

これは異なります。マクロはプリプロセッサが文字列的に置き換えてしまうため、objファイル内のシンポル・テーブルへ出力されません。その結果、IDE等でポイントしても置き換え前の文字列が表示されることはないです。
しかし、constで宣言した場合は、シンポル・テーブルへ出力されIDEにて表示されます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/20 22:14

    ご回答ありがとうございます。
    大変参考になりました。

    また、私も今回、C++のconstの内部結合と外部結合を知って、少し驚いておりました。

    ところで、別々のcppでインクルードされても、リンカによって最適化されるなら
    問題はないのですが、もし、それぞれの変数の領域が確保されるなら、
    無駄な領域が増えていくので、あまりよくなさそうには思いました。
    なので、この点について、詳細を知りたいと思っておりました。

    あと、確かにマクロとは違うかと思います。
    ただ、どこかで見かけたのですが、const変数として宣言されている場合、
    C++は、場合によっては、変数領域を確保しないみたいな記述が
    あったような気がします。
    再度ネット上を検索したのですが、見つかりませんでした。
    なので、こういった点をもう少しクリアにできればと思っておりました。

    キャンセル

  • 2016/05/20 22:33 編集

    MinGW(gcc)で、g_Testのアドレスを表示してみたら、a.cppとb.cppで異なるアドレスが表示されました。
    この場合は、異なる領域を確保されているということです。

    しかし、これってシュレディンガーの猫のようです。
    アドレスを表示しているa.exeをnmコマンドでみたところ、__ZL6g_Testというシンボルが2つ出力され、表示されたアドレスと同じ値が割り当てられていました。
    しかし、値を表示するだけでアドレス表示をやめると、nmコマンドで出力されなくなりました。つまり、領域が割り当てられていないようです。

    > もし、それぞれの変数の領域が確保されるなら、無駄な領域が増えていくので、あまりよくなさそうには思いました。

    正直、誤差と思います。もし、配列で巨大な領域を確保するのでしたら、明示的に同じ領域を使うべきと思います。普通にconst変数として定義するのであれば、1,000個2,000個のような数の定義はなかなか厳しいものがあります。そして、仮に10,000個定義したとしても高々40KBytesです。

    もし、組み込み系でそのような贅沢が許されない場合は、extern宣言等を使って領域を確実に1つしか消費しないようにするのが妥当と思います。

    キャンセル

  • 2016/05/21 13:56

    コメントありがとうございます。

    変数のアドレスを表示していると、異なる領域が確保されるが、
    アドレス表示をやめると、コンパイラが領域確保が必要ないと
    判断して、領域確保をしないということですよね。

    つまり、const変数の使用状況によって、領域が確保されたり
    されなかったりする、ということだと思います。

    また、誤差の範囲にすぎないという点も大変参考になりました。
    確かに微々たるものではあるかと思います。

    今までの仕事では、どちらかというと組み込み系的な仕事が
    最初は多かったので、マクロではなくて、const変数で
    それぞれのcppに領域が確保されそうなことに
    違和感を感じたのですが、今後は、プラットフォームや
    作業のしやすさなどを考慮して、マクロやconstの利用を
    決めて行ければと思っております。

    ご教示ありがとうございました。

    キャンセル

0

C++でもそうですが、言語の規格は、「どのように動く」かを定義するもので、「それをどのように実装すべきか」は、必要がなければ規定していません。

で、「const intが内部結合」ということは、「この変数は当該ファイル外からは使われ得ない」ということになります。

const volatileで最適化が阻止されているとか、ポインタを取るので実体の伴った変数として必要ということでなければ、この変数の代わりに直接値を書いても何も問題ありません。つまり、ファイル内を一通り見て最適化するとまずい使い方がなければ、定数として畳み込んでしまっても動作は全く同じです。変数として領域を確保するまでもありません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/20 22:19

    ご回答ありがとうございます。
    大変参考になりました。

    ということは、コンパイラによっては、const変数の使われ方次第で、
    変数として領域を確保せずに、処理してしまう可能性がある
    ということですよね。

    了解致しました。
    ありがとうございました。

    キャンセル

  • 2016/05/20 22:31 編集

    一般的なC言語のコンパイラの場合、外部結合するものは除いて、コンパイル後のオブジェクトコードはレジスタとメモリを直接扱うものです。つまり、(デバッグ情報で紐付けなければ)「変数名」という概念は雲散霧消しています。同じレジスタやメモリを、C言語では複数の変数だったものに流用しても、「ソースコードのとおりに動作するなら」一向に構いません。

    キャンセル

  • 2016/05/20 22:35

    ってことは extern const int foo; はありえないと。
    # それにしちゃ大抵の処理系でコンパイル・エラーにはならんのよね ^^;

    キャンセル

  • 2016/05/20 22:39

    extern constとしたときはきちんと外部結合になる、とのことです。

    キャンセル

  • 2016/05/20 22:45

    とはいえ リンク時にコケますよね? 内部結合なら複数のファイルで異なる値で
    const int foo = ほげほげ; できちゃうだから。

    キャンセル

  • 2016/05/21 12:28 編集

    extern constを使う場合は、ふつうの変数同様にヘッダでは「extern const int foo;」として、1つのソースでこのヘッダを入れたうえでソースで「const int foo = 5;」とするのが正式のようです。

    キャンセル

  • 2016/05/21 13:44

    ご教示ありがとうございます。
    コメントを拝見しながら考ええいたのですが、
    私が変数の領域が確保されるかどうかと書きましたが、
    メモリが確保されるかどうかと書いた方がより
    正確だったのかもしれない、と思いました。

    キャンセル

0

内部結合なので、a.cppとb.cppのそれぞれの中で、 
別々に、変数の領域が確保されるような気がしました。 

VC++では実際にそうなります。一応確認のため複数のソースに分けて書いた関数で変数のポインタを表示させたところ、それぞれ異なるアドレスになっていました。

ちなみに、const int g_Test = 0;static const int g_Test = 0;でアセンブリソースを出力して比較しましたが、VC++では全く同じコードでした。

マクロと同等のものとして扱われるか?、 

全く別物です。マクロは単なるテキストの置き換えですが、constグローバル変数は厳密に型を持った読み取り専用の変数として扱われます。

const付きのグローバル変数は、領域へのアクセス(ポインタ経由の参照など)を行わずに値を利用するだけなら、コンパイラーが使用箇所で数値に置き換えます。その場合、領域が確保されることもないので、結果的にはマクロで書いたのと同じような振る舞いに見えます。

ただし、単純な数値の置き換えができないような非組み込み型の場合は領域が確保されます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/05/21 13:42

    ご回答ありがとうございます。
    大変参考になりました。

    実施にアドレスを表示してみたら、異なるアドレスだったのですね。

    また、マクロではないことと、コンパイラが場合によっては、
    使用箇所を数値に置き換えてしまうので、領域が確保されない
    という点も大変参考なりました。

    ご教示ありがとうございました。

    キャンセル

関連した質問

同じタグがついた質問を見る

  • C++

    3608questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。