K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[1019] externについて
投稿者:ひげおやじ
2007/08/20 15:07:24

「C言語ポインタ完全制覇」を読ませてもらっております。 その本の本筋とは外れるのかしれませんが、第5章のヘッダファイルの作成と関連 してexternでわからない点がいくつかあるので質問させてください。 ご回答いただけると幸いです。 とりあえず、ひとつお聞きします。 分割コンパイルなどで、外部で定義している変数を参照するにはexternを使う、と よく本に書いてあると思いますが、外部の関数を参照するにはexternを 付ける必要がないように見受けられるのです。これはなぜなんでしょうか。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1020] Re:externについて
投稿者:774RR
2007/08/20 20:09:49

そういう約束だから 関数(原型)宣言に extern も static もない場合は extern が補われると考えよう void myfunc(int); // これには extern も static も明示されてないので extern void myfunc(int); // と解釈される規則 この規則は関数の場合にのみ適用されるので、変数宣言には適用されない 以下は、言語規格書の厳密解釈なので、一読してわからなかったらスキップしてね ・関数宣言で指定できる linkage は static か extern かどちらかだけ (無指定も OK) ・ static が明示されている関数宣言は internal linkage ・ internal linkage でない関数宣言は external linkage ヘッダ中で宣言+ソースファイル中で定義することは完璧に正しい ----hoge.h---- void hogefunc(double); // This declaration has external linkage ----hoge.c---- #include "hoge.h" void hogefunc(double d) { ... } // This definition also has external linkage 以下のような小細工コードをいまだに見るけど、現代 C/C++ であれば不要 #ifdef HOGE_DEFINITION #define EXTERN #else #define EXTERN extern #endif
[この投稿を含むスレッドを表示] [この投稿を削除]
[1021] Re:externについて
投稿者:(ぱ)こと管理人
2007/08/20 22:51:43

>分割コンパイルなどで、外部で定義している変数を参照するにはexternを使う、と >よく本に書いてあると思いますが、外部の関数を参照するにはexternを >付ける必要がないように見受けられるのです。これはなぜなんでしょうか。 774RRさんのおっしゃるようにそういう約束だから…なのですが(X3010の6.1.2.2)それを言ってしまうとつまらないので、理由を考えますと。 大規模なプログラムを何人もの人で書いている場合、グローバル変数の名前が偶然かち合ってしまう可能性があります。たとえば a.c: int hoge; b.c: int hoge; と書いてもエラーにならないと困ってしまいます。この場合、それぞれの担当プログラマには、「グローバル変数の値がいつの間にか変わっている」という得体の知れないバグとして見えるはずです。 その点、「externなしで定義できるのは必ず1箇所のみ、それ以外の箇所では必ずexternを書け」という規則にしておけば、リンク時にエラーになるのでこの問題を回避することができます。 では関数の場合はどうかというと、関数名が偶然かち合ったとして、 a.c: int hoge(void) {...} b.c: int hoge(void) {...} とか書いたらどうせエラーです。 つまり、関数の場合、関数の処理を書くブロックの有無で定義と宣言の区別がつくため、わざわざexternをつける必要はない、というのが私の理解です。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1022] Re:externについて
投稿者:(ぱ)こと管理人
2007/08/20 22:56:45

>ヘッダ中で宣言+ソースファイル中で定義することは完璧に正しい これはもちろんそうなのですが、 >以下のような小細工コードをいまだに見るけど、現代 C/C++ であれば不要 >#ifdef HOGE_DEFINITION >#define EXTERN >#else >#define EXTERN extern >#endif この小細工は、ヘッダとソースファイルに似たようなものを分散させたくないという面からは有効だと思いますよ。初期化子がうまく書けないとか、文法的にCのパーサを通らんようなマクロを作るなとか、そもそもそんなにグローバル変数使うなよとか、批判があるのはわかりますが。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1023] Re:externについて
投稿者:774RR
2007/08/21 00:03:20

>グローバル変数の名前が偶然かち合ってしまう可能性があります。 (snip) >と書いてもエラーにならないと困ってしまいます。 C++ においては ODR (One Definition Rule) により、これは必ずエラーになる C ではエラーにならない (仮定義の重複を認める) という違いがあるですな。 初心者向け解説として「 extern は別のところにあるものを使う宣言」と書いてある書籍を見かけるけど 言語規格書的には大きく間違い (同一翻訳単位中にあってもかまわないので) 俺も後輩君にそういう説明したことあるし反省っすな。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1024] Re:externについて
投稿者:ひげおやじ
2007/08/21 03:24:05

ご回答ありがとうございますm(_ _)m 変数と関数の宣言・定義の違いでの説明がしっくり来ました。 >初心者向け解説として「 extern は別のところにあるものを使う宣言」と書いてある書籍を見かけるけど >言語規格書的には大きく間違い (同一翻訳単位中にあってもかまわないので) まさにこれがもう一つ質問したかったことでした。 「別のところにあるのを宣言する」ってのはexternの本質を表していないのですね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1025] Re:externについて
投稿者:774RR
2007/08/21 08:37:50

>「別のところにあるのを宣言する」ってのはexternの本質を表していないのですね。 こういう疑問が出てくるということは、言語規格書を読んでみていいレベルということだ 初心者向け解説書は厳密さよりもわかりやすさを優先せざるを得ないので びみょーに不正確だったりすることがある extern の意味するところは[1020]で書いたとおり、結合指定 C なら JIS X 3010:2003 6.2.2 C++ なら JIS X 3014:2003 3.5 厳密なところは自分で JIS 言語規格書を参照してもらうとして、簡単に言うなら extern:外部結合を指定、static:内部結合を指定 外部結合=その名前が表す実体は、他の翻訳単位または同じ翻訳単位で使うことができる 内部結合=その名前が示す実体は、同じ翻訳単位で使うことができる ということだ。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1030] Re:externについて
投稿者:yuya
2007/08/25 08:46:08

>以下のような小細工コードをいまだに見るけど、現代 C/C++ であれば不要 >#ifdef HOGE_DEFINITION >#define EXTERN >#else >#define EXTERN extern >#endif 昔のCを知らないので、教えていただきたいのですが、 かつてはexternが「同一翻訳単位外のみ」を意味していたのでしょうか?
[この投稿を含むスレッドを表示] [この投稿を削除]
[1032] 管理者により削除されました
2007/08/30 03:03:18

引用のみの投稿だったため削除しました。
[この投稿を含むスレッドを表示]
[1033] Re:externについて
投稿者:774RR
2007/08/25 21:38:09

>かつてはexternが「同一翻訳単位外のみ」を意味していたのでしょうか? かつてというのがどのくらい昔の話を意図してるのかわからないけど、 少なくともC89 (ISO/IEC 9899:1990) の文書には今と同じ[結合]の記述がある。 規格採択前の独自仕様の処理系については、もう知る必要も無いと思う。 俺は採択前の処理系も複数個実用してたけど、「外のみ」は無かったと追記しとく。 この小細工はexternとは同一翻訳単位外のものを取り扱うものだと 「誤解したプログラマが」書いてたもので 言語仕様を理解していたプログラマなら書かない代物。 あるいは[1022]前橋さんのコメントのように 宣言部と定義部で類似の記述を分散させるのを嫌ったプログラマが行っていたかもしれない。 ----hoge.h---- extern int hoge; ----hoge.c---- #include "hoge.h" int hoge; // これならここに初期値を書ける -------------- hoge の記述がヘッダとソースで2回出てくるのはミスの元という人がいる 以下のように書いておけば記述が2回必要ないというわけで ----piyo.h---- #ifndef EXTERNPIYO #define EXTERNPIYO extern #endif EXTERNPIYO int piyo; ----piyo.c---- #define EXTERNPIYO #include "piyo.h" // piyo の初期値を与える手段が無い -------------- んで [1022] に既述のごとく初期化子がうまく書けないとか問題があるんで俺はやらない。 ちなみにC++ではODRがあるので「定義にならない宣言」と「定義」は分離するのが定石。 類似の既述が2回でてくるのは仕方ない。類似は同一ではないのだから。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1034] Re:externについて
投稿者:yuya
2007/08/26 16:41:01

774RRさん、ありがとうございます。 「現代 C/C++ では不要」とあったので、 古い処理系でも通るようにするための小細工なのかな、 と思ったんですが、「誤解の産物」だったんですね。 誤解されたままで、このヘッダが 外部定義か仮定義のあるファイルにインクルードされてEXTERNが消えても、 それに続く部分は仮定義になるので (つまり、外部定義 + 仮定義になるか、仮定義 + 仮定義になり、 どちらにしても「外部定義がただひとつ」と等価になる) 実害はなかった、ということになるのでしょうか。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1035] Re:externについて
投稿者:774RR
2007/08/27 08:32:29

[結合]を理解していないプログラマが[仮定義]を理解していて、 意識してこういうコードを書いたかという点については、俺はきわめて疑問に思う。 たまたま実害が無かったとしても「間違っているが結果オーライ」なだけだろう。 繰り返すが C++ には[仮定義]は無いのでこーいう書き方はダメ。 [1020]と[1033]のサンプルは変えてあるのだけど、ちゃんと見てるよね。 [1020]は実際には一行抜けてて #undef EXTERN が #ifdef の前に必要 [1033]は EXTERNHOGE と EXTERNPIYO と使い分けて名前がかぶらない 両者ともいちおう正しく使えば[仮定義]も重複しないようになってるつもり。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1036] Re:externについて
投稿者:yuya
2007/08/27 17:08:34

> [結合]を理解していないプログラマが[仮定義]を理解していて、 > 意識してこういうコードを書いたかという点については、俺はきわめて疑問に思う。 > たまたま実害が無かったとしても「間違っているが結果オーライ」なだけだろう。 全面的に同意します。前回もそういうつもりで書きました。 実害が出てくれりゃ勉強する機会も生まれただろうに、と。 > [1020]と[1033]のサンプルは変えてあるのだけど、ちゃんと見てるよね。 はい、ちゃんと見てますです(コメントしておらず失敬しました)。 複数のヘッダファイルをインクルードする状況では、 [1020]だと#undefしとかないと重複定義になっちゃいますね。 でもって、[1035]のようにEXTERNの名前を使い分けるなら、 わざわざHOGE_DEFINITIONだのPIYO_DEFINITIONだのを別に用意する必要はないわけですね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1037] Re:externについて
投稿者:(ぱ)こと管理人
2007/08/28 01:38:14

山奥に旅に出ているうちに掲示板が進んでました。 >この小細工はexternとは同一翻訳単位外のものを取り扱うものだと >「誤解したプログラマが」書いてたもので >言語仕様を理解していたプログラマなら書かない代物。 どうでしょうか。私が最初にコレ↓を書いた時点では、 http://kmaebashi.com/programmer/pointer.html 細かい仕様を理解していたかどうかはともかく、extern付きのとそうでないのを 同一コンパイル単位に書いてよい、ということは経験から知っていたと思いますよ (昔のことですし、証拠を出せといわれても困りますが)。 ていうか、変数定義を入れたdefvar.cとextern宣言を入れたextern.hがあったとして、 defvar.cだけextern.hを#includeしないことは容易です。 その場合嬉しくないこととしては以下の2点があって、 (1)defvar.cとextern.hの間でコンパイラのチェックが働かなくなる。 (2)似たようなことを2箇所に書かなければならない。 774RRさんの説は、(1)を問題視したプログラマがこの小細工を使った、 というものですよね。(2)はいずれにせよ問題になるんですから。 (1)の問題は、int hoge[100];とint *hoge;みたいな問題を起こすので 確かに恐ろしいんですが、(2)の問題も存在するわけですから、 (1)の問題のため「だけ」にこの小細工が使われた、というのは無理があるように 思います。 ・この小細工は、(1)の問題の解決には役に立たない(役に立つと思っている  人がいるとしたら、Cの仕様を知らないだけ)。 ・(2)の問題の解決にはなるが、初期化子が書けなくなるしパーサ通らなくなる  マクロの使い方なのであまり薦められたもんではない。 ということであれば同意します。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1038] Re:externについて
投稿者:yuya
2007/08/28 07:16:42

どうも自分が議論について行っているつもりで 実は勘違いしているような気がしてきて不安なのですが……。 まず、この議論には この小細工を施したヘッダを『すでに定義を書いてあるファイル』に インクルードしているようなソースが世の中に(けっこう)存在する という大前提がありますよね。その上での話ですが、 > 774RRさんの説は、(1)を問題視したプログラマがこの小細工を使った、 > というものですよね。(2)はいずれにせよ問題になるんですから。 確かに宣言自体を削らずにわざわざexternだけ削っているということは コンパイラのチェックを気にしているとも考えられますが、 単に「ここではまずいから、externを削っとこう」くらいの人もいるような気がします。 誰かが(2)の目的で書いたものを見て、 主旨を誤解して使っている(で、同じことをばっちり2回書いている)というのは あり得そうですね。 >・この小細工は、(1)の問題の解決には役に立たない えーと、これは「小細工を施してもコンパイラはチェックしてくれない」という意味ではなく、 「チェックしてくれるのは小細工のおかげではない」という意味ですよね?
[この投稿を含むスレッドを表示] [この投稿を削除]
[1039] Re:externについて
投稿者:(ぱ)こと管理人
2007/08/29 00:34:30

>この小細工を施したヘッダを『すでに定義を書いてあるファイル』に >インクルードしているようなソースが世の中に(けっこう)存在する > >という大前提がありますよね。 えっ? …と読み返したら、yuyaさんの[1034]には確かにそう書いてありました。 (すみません、よく読んでませんでした) この小細工を使うときには、.cにはそもそも定義を書かないと思います。 .cに定義を書いたらメリットがまったくありませんので。 >>・この小細工は、(1)の問題の解決には役に立たない >えーと、これは「小細工を施してもコンパイラはチェックしてくれない」と >いう意味ではなく、「チェックしてくれるのは小細工のおかげではない」と >いう意味ですよね? そうです。 小細工を行わないとき、方法はふたつあって、[1037]で言うところの a)defvar.cに定義を書き、extern.hに宣言を書く。  defvar.cではextern.hを#includeする。 b)defvar.cに定義を書き、extern.hに宣言を書く。  defvar.cではextern.hを#includeしない。 a)なら型チェックが効きますが、b)では効きません。 774RRさんがおっしゃるような、 >この小細工はexternとは同一翻訳単位外のものを取り扱うものだと >「誤解したプログラマ なら、a)はそもそも書けないと思い込んでいるから、型チェックのため 小細工を行った、というのが、私が書いた 「(1)を問題視したプログラマがこの小細工を使った」 の意図です。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1040] 管理者により削除されました
2007/08/30 03:04:15

引用のみの投稿だったため削除しました。 …ていうか何の嫌がらせよ。
[この投稿を含むスレッドを表示]
[1041] 管理者により削除されました
2007/08/30 03:03:40

引用のみの投稿だったため削除しました。
[この投稿を含むスレッドを表示]