[1042] externと「外部結合」
投稿者:yuya
2007/08/30 16:42:41
別スレッドを立てました。
externと「外部結合」については以前から私の悩みの種で、
現時点で以下のように理解しているのですが、問題がないかどうか、よろしければご意見をお聞かせください。
以下、すべてブロック外の話で、「宣言」とは「定義でない宣言」を指すとします。
extern宣言は、同一翻訳単位内にすでに定義があれば、その結合に従った宣言となります。
定義がなければ他の翻訳単位に定義があるだろうと仮定して、
それに対応する宣言になろうとします(この場合には、状況から外部結合しかあり得ません)。
このようにextern宣言の振る舞いを見ると、externは
「近いところから順に定義を探し、状況に合わせて(きわめて自然に)結合が定まる」
宣言であると考えられます。
もちろん、実際にはほとんどのケースで外部結合になり、
内部結合になるのは同一翻訳単位内にstatic定義がある場合だけですが、
これを「例外」と捉えるよりも、上記のように結合が定まると考えたほうが良いように思います。
初期化子のない変数定義・宣言を考えたとき、記憶クラス指定子をつけなければ外部結合の仮定義になりますが、
これが(他に定義があるために)宣言に成り下がった場合でも、外部結合であることは動きません。
したがって、
static int hoge = 1; /* 定義(内部結合) */
int hoge; /* 仮定義(外部結合)、ここでは宣言になるが、結合指定が定義と矛盾する */
は未定義動作になります。これに対し、2行目にexternをつけた
static int hoge = 1; /* 定義(内部結合) */
extern int hoge; /* 宣言(結合は状況に応じる)、ここでは定義に従い内部結合となる */
は問題ありません(ソースを比較すると、後者のほうが「問題あり」に見えてしまいますが)。
これを「結合」という観点から見ると、「絶対に外部結合」だったものが、
externを付けることで「結合は状況に合わせる」に変わっています。
ではexternの実際の機能は何かというと、
「私は定義ではなく、ただの宣言である。定義はほかにある」ことを明示することにあります。
[1024]ひげおやじさんの
> 「別のところにあるのを宣言する」ってのはexternの本質を表していないのですね。
において、「別のところ」というのが「翻訳単位外」を指しているならば、確かに誤りです。
しかし、その「誤りである理由」は、「外部結合が『翻訳単位内も含む』概念だから」ではなく、
「externが(結合とは無関係に)翻訳単位内外両方に定義を探しに行くから」ではないでしょうか。
そもそもリンケージが翻訳単位外に及ぶ(つまり外部結合)、というのと、
宣言が定義を探しに行く範囲が翻訳単位外に及ぶ(extern宣言)、というのは
分けて考えるべきだと思うのですが、皆様のご意見はいかがでしょうか?
なお、ブロック外において明らかに定義と分かるもの(関数定義や、初期化子の付いた変数定義)に
externが付いた場合、外部結合になりますね(付けなくても同じですが)。
この場合はexternが文字通り「外部結合」を意味していると考えられますが、
ANSI以前はコンパイルエラーになっていたと聞いたことがあります。