
C言語(主にVC++)で開発をしていると、よく、
const char MSG_HELLO[] = "HELLO!";
のように、ヘッダファイル中で、文字列リテラルを定義します。
自分もいつのまにかこのようにリソース定義するようになっていましたが、初期の頃から疑問だったのは、extern宣言せずにchar配列を宣言しているから、コンパイラが最適化しなかったら、上記宣言のあるヘッダファイルを読み込んで、その定義を使った個所のあるソース分文字列リテラルがメモリ上に確保されてしまうのではないか、、?ということです。
つまり、本来なら
const char* MSG_HELLO[] = "HELLO!";
とソースファイル中で定義し、ヘッダファイルには
extern const char* MSG_HELLO;
とすべきではないか、と思うわけです。ただ実際にこれをやっているとヘッダとソースの両方のメンテナンスが必要になるので、冒頭のように記述しているのだと思いますが。
この、本来なら下記のようにすべきだが、コンパイラの最適化(リソースのプール)を期待して最初のように書いている、という解釈は正しいでしょうか?
No.3ベストアンサー
- 回答日時:
ANSI以前のC言語において「外部リファレンスを持つ変数オブジェクト」の領域は『あいまいなDEF/REF規約』によって規定されていました。
あいまいなDEF/REF規約とは「定義(Define)と参照(Reference)が明確に分離していない」ということで、
・ブロック外定義の変数/配列は記憶クラスを省略するとexternとなる
・複数のextern宣言された変数/配列オブジェクトが存在する場合、1つの実態が確保される
という規約によって正当化されます。
結果、ご質問のようなコードはリンカによって1つの実体オブジェクトになることが保証されていました。
しかし、現在のC言語規約では「明確なDEF/REF規約」が用いられているため、ブロック外で記憶クラスを省略した同一名称の変数/配列オブジェクトを複数宣言することは推奨されません。
「明確なDEF/REF規約」に準拠した処理系ではリンカエラーになります。
「あいまいなDEF/REF規約」をサポートした処理系ではワーニングが発生した上で、ANSI以前のCコンパイラと同様の配置を行います。
static記憶クラスで宣言していない限り、同一名称の変数/配列オブジェクトが複数配置されることは(外部リファレンスを持つオブジェクトでは)ありえません。
なるほど、、そういう規約があったんですね。ではVCはあいまなDEF/REF規約をサポートしているんですね、、
ということはやはり最新の仕様ではヘッダファイルで文字列リテラルを定義するのは望ましくないと思うのですが、では実際どのようにすればいいのでしょう、、yatokesaさんがご指摘の通りVCではリソースファイルのストリングテーブルを使うのが望ましいかもしれませんが、プラットフォーム依存になってしまう点と、rcファイルは扱いにくい(リソースエディタを開くのも面倒)という点から、つかっていません。やはりヘッダとソースにわけるのが正しいのでしょうか、、これもメンテナンスが大変になるので避けたいのですが、、
なお質問中の
const char* MSG_HELLO[] = "HELLO!";
は
onst char* MSG_HELLO = "HELLO!";
と訂正させて頂きます。
ご回答ありがとうございました。
No.4
- 回答日時:
>ファイル冒頭で#ifdefで区切っても各オブジェクトファイルでオブジェクトが確保される点は変わらないと思い ます、、
#ifdef __MAIN__
const char* MSG_HELLO = "HELLO!";
#else
extern const char* MSG_HELLO;
#endif
mainのあるソースのみ #define __MAIN__ のようにする。オブジェクトは1つで済みますよね?
私の常套手段です。が、これが増えてくるとメンテナンスが大変になってきますが...。
なるほど、、! これはアイデアですね。
参考にさせて頂きます。
ソースとヘッダにわける以外では、最善策かもしれませんね、、悩ましいです
No.2
- 回答日時:
私ならリテラル文字列はヘッダファイルで
#define
か、リソースです。
ちなみに、C++でヘッダファイル中に
const char* MSG_HELLO[] = "HELLO!";
を書くとリンクでエラーになったような・・・
#defineで区切ると、各部に文字列リテラルが直接埋め込まれてしまうので、コンパイラの最適化がないとそれこそ大変なことになる気がして使っていません。
たしかにQuickCでやっていた頃は、ヘッダで定義するとエラーになった記憶があります。このあたりはtoysmithさんがお答えになっている通りなのでしょうね、、
ご回答ありがとうございました。
No.1
- 回答日時:
回答ではないですが...
#最近のコンパイラ・リンカの事情には詳しくはないので
少なくとも、コンパイラの最適化ではないですよね。コンパイラはオブジェクトを作り、その中に "HELLO!"分の領域はどのオブジェクトにもあると思います。
リンカが最適化によって上記領域をまとめるか否かはよくわかりません。私の認識では、ロードモジュールになったときにも別の領域になっていると考えてます。#リンクマップを見ればわかるのかな?
メンテナンス性という意味で、#define と #ifdefを組み合わせればそれほど苦では無いと思います。量が多ければ大変ですが...。VCなら rcに文字列をまとめた方が行儀がよいのかもしれませんね。#私はやってませんが...^^;)
たしかにコンパイル完了時点では、各オブジェクトファイル内に各文字列オブジェクトがあり、リンク時にまとめられるのでしょうね、、私もそのあたりもう少し勉強しなければ。
ファイル冒頭で#ifdefで区切っても各オブジェクトファイルでオブジェクトが確保される点は変わらないと思います、、
ご回答ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# const char** p;のとき、free(p)でC4090エラーとなるのはなぜですか 3 2023/03/31 16:28
- C言語・C++・C# C言語初心者 構造体 課題について 2 2023/03/10 19:48
- C言語・C++・C# C言語 少しの疑問 4 2022/11/08 02:48
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# c言語 コマンドライン引数 4 2023/02/09 18:47
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# スタックフレームの消滅 6 2023/05/20 12:33
- C言語・C++・C# C言語について質問です。 以下のような結果を出すためにはどのようなコードが良いですか?サンプルコード 3 2022/11/01 16:19
- C言語・C++・C# 【C言語】全角文字の配列を、全角のまま1文字ずつ出力する方法 4 2023/05/09 15:08
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・「それ、メッセージ花火でわざわざ伝えること?」
- ・ゆるやかでぃべーと すべての高校生はアルバイトをするべきだ。
- ・【お題】甲子園での思い出の残し方
- ・【お題】動物のキャッチフレーズ
- ・人生で一番思い出に残ってる靴
- ・これ何て呼びますか Part2
- ・スタッフと宿泊客が全員斜め上を行くホテルのレビュー
- ・あなたが好きな本屋さんを教えてください
- ・かっこよく答えてください!!
- ・一回も披露したことのない豆知識
- ・ショボ短歌会
- ・いちばん失敗した人決定戦
- ・性格悪い人が優勝
- ・最速怪談選手権
- ・限定しりとり
- ・性格いい人が優勝
- ・これ何て呼びますか
- ・チョコミントアイス
- ・単二電池
- ・初めて自分の家と他人の家が違う、と意識した時
- ・「これはヤバかったな」という遅刻エピソード
- ・ゴリラ向け動画サイト「ウホウホ動画」にありがちなこと
- ・泣きながら食べたご飯の思い出
- ・一番好きなみそ汁の具材は?
- ・人生で一番お金がなかったとき
- ・カラオケの鉄板ソング
- ・自分用のお土産
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Excelで =EMBED("Acrobat Docu...
-
C#でフォームのオブジェクト名...
-
パワーポイントのVBAでテキスト...
-
VBA 同じ名前のオブジェクトを...
-
LDAPにアクセスしてNamingEnume...
-
ビジュアルC++でボタンの有...
-
VBAのWindowオブジェクトとWork...
-
オブジェクトレベルとメタレベル
-
C++でのクラスオブジェクトの破棄
-
VBSでのステートメントの末尾が...
-
printerオブジェクトでのテキス...
-
SQLException
-
JAVAからHTMLへ値を返す方法
-
設定したGDIオブジェクトを必ず...
-
Accessの連結・非連結オブジェ...
-
Struts Listの情報がgetできない。
-
JSPのスレッドセーフについて
-
java.util.Listのヒープサイズ...
-
質問すいません。 javascriptの...
-
Object型からDouble型へのキャスト
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Excelで =EMBED("Acrobat Docu...
-
パワーポイントのVBAでテキスト...
-
EXCEL VBAにて動的にCheckBOXを...
-
VBA 同じ名前のオブジェクトを...
-
VBAのWindowオブジェクトとWork...
-
ワイルドカード<?>と型パラメー...
-
C#でフォームのオブジェクト名...
-
ビジュアルC++でボタンの有...
-
戻り値がクラスオブジェクト
-
Object型からDouble型へのキャスト
-
COMコンポーネントって何?
-
error C2712: オブジェクト ア...
-
CoCreateInstanceでエラーになる。
-
LISTBOXの内容が更新されま...
-
Accessの連結・非連結オブジェ...
-
オブジェクトレベルとメタレベル
-
多人数のじゃんけんプログラム
-
C++でのクラスオブジェクトの破棄
-
.getElementById()のエラーにつ...
-
Rangeオブジェクトを一時的に作...
おすすめ情報