プロが教えるわが家の防犯対策術!

C言語(主にVC++)で開発をしていると、よく、

const char MSG_HELLO[] = "HELLO!";

のように、ヘッダファイル中で、文字列リテラルを定義します。
自分もいつのまにかこのようにリソース定義するようになっていましたが、初期の頃から疑問だったのは、extern宣言せずにchar配列を宣言しているから、コンパイラが最適化しなかったら、上記宣言のあるヘッダファイルを読み込んで、その定義を使った個所のあるソース分文字列リテラルがメモリ上に確保されてしまうのではないか、、?ということです。
つまり、本来なら

const char* MSG_HELLO[] = "HELLO!";

とソースファイル中で定義し、ヘッダファイルには

extern const char* MSG_HELLO;

とすべきではないか、と思うわけです。ただ実際にこれをやっているとヘッダとソースの両方のメンテナンスが必要になるので、冒頭のように記述しているのだと思いますが。
この、本来なら下記のようにすべきだが、コンパイラの最適化(リソースのプール)を期待して最初のように書いている、という解釈は正しいでしょうか?

A 回答 (4件)

ANSI以前のC言語において「外部リファレンスを持つ変数オブジェクト」の領域は『あいまいなDEF/REF規約』によって規定されていました。



あいまいなDEF/REF規約とは「定義(Define)と参照(Reference)が明確に分離していない」ということで、
・ブロック外定義の変数/配列は記憶クラスを省略するとexternとなる
・複数のextern宣言された変数/配列オブジェクトが存在する場合、1つの実態が確保される
という規約によって正当化されます。
結果、ご質問のようなコードはリンカによって1つの実体オブジェクトになることが保証されていました。

しかし、現在のC言語規約では「明確なDEF/REF規約」が用いられているため、ブロック外で記憶クラスを省略した同一名称の変数/配列オブジェクトを複数宣言することは推奨されません。
「明確なDEF/REF規約」に準拠した処理系ではリンカエラーになります。
「あいまいなDEF/REF規約」をサポートした処理系ではワーニングが発生した上で、ANSI以前のCコンパイラと同様の配置を行います。

static記憶クラスで宣言していない限り、同一名称の変数/配列オブジェクトが複数配置されることは(外部リファレンスを持つオブジェクトでは)ありえません。
    • good
    • 0
この回答へのお礼

なるほど、、そういう規約があったんですね。ではVCはあいまなDEF/REF規約をサポートしているんですね、、
ということはやはり最新の仕様ではヘッダファイルで文字列リテラルを定義するのは望ましくないと思うのですが、では実際どのようにすればいいのでしょう、、yatokesaさんがご指摘の通りVCではリソースファイルのストリングテーブルを使うのが望ましいかもしれませんが、プラットフォーム依存になってしまう点と、rcファイルは扱いにくい(リソースエディタを開くのも面倒)という点から、つかっていません。やはりヘッダとソースにわけるのが正しいのでしょうか、、これもメンテナンスが大変になるので避けたいのですが、、
なお質問中の
const char* MSG_HELLO[] = "HELLO!";

onst char* MSG_HELLO = "HELLO!";
と訂正させて頂きます。
ご回答ありがとうございました。

お礼日時:2002/06/26 08:46

>ファイル冒頭で#ifdefで区切っても各オブジェクトファイルでオブジェクトが確保される点は変わらないと思い ます、、



#ifdef __MAIN__
const char* MSG_HELLO = "HELLO!";
#else
extern const char* MSG_HELLO;
#endif

mainのあるソースのみ #define __MAIN__ のようにする。オブジェクトは1つで済みますよね?
私の常套手段です。が、これが増えてくるとメンテナンスが大変になってきますが...。
    • good
    • 1
この回答へのお礼

なるほど、、! これはアイデアですね。
参考にさせて頂きます。
ソースとヘッダにわける以外では、最善策かもしれませんね、、悩ましいです

お礼日時:2002/06/26 12:49

私ならリテラル文字列はヘッダファイルで


#define
か、リソースです。

ちなみに、C++でヘッダファイル中に
const char* MSG_HELLO[] = "HELLO!";
を書くとリンクでエラーになったような・・・
    • good
    • 1
この回答へのお礼

#defineで区切ると、各部に文字列リテラルが直接埋め込まれてしまうので、コンパイラの最適化がないとそれこそ大変なことになる気がして使っていません。
たしかにQuickCでやっていた頃は、ヘッダで定義するとエラーになった記憶があります。このあたりはtoysmithさんがお答えになっている通りなのでしょうね、、
ご回答ありがとうございました。

お礼日時:2002/06/26 08:41

回答ではないですが...


#最近のコンパイラ・リンカの事情には詳しくはないので

少なくとも、コンパイラの最適化ではないですよね。コンパイラはオブジェクトを作り、その中に "HELLO!"分の領域はどのオブジェクトにもあると思います。

リンカが最適化によって上記領域をまとめるか否かはよくわかりません。私の認識では、ロードモジュールになったときにも別の領域になっていると考えてます。#リンクマップを見ればわかるのかな?

メンテナンス性という意味で、#define と #ifdefを組み合わせればそれほど苦では無いと思います。量が多ければ大変ですが...。VCなら rcに文字列をまとめた方が行儀がよいのかもしれませんね。#私はやってませんが...^^;)
    • good
    • 0
この回答へのお礼

たしかにコンパイル完了時点では、各オブジェクトファイル内に各文字列オブジェクトがあり、リンク時にまとめられるのでしょうね、、私もそのあたりもう少し勉強しなければ。
ファイル冒頭で#ifdefで区切っても各オブジェクトファイルでオブジェクトが確保される点は変わらないと思います、、
ご回答ありがとうございました。

お礼日時:2002/06/26 08:38

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!

このQ&Aを見た人はこんなQ&Aも見ています