K.Maebashi's BBS

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

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

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

[1056] 管理者により削除されました
2007/09/12 23:12:53

エロ系SPAMだったので削除しました。
[この投稿を含むスレッドを表示]
[1055] Re:externと「外部結合」
投稿者:yuya
2007/09/06 15:20:34

> Rationaleを見ても、このへんに関する記述は以下の箇所くらいのようです。 > http://www.lysator.liu.se/c/rat/c1.html#3-1-2-2 rationaleにこんな記述があったのですね。ありがとうございます。 > extern int i3 = 3; > これも不可解ですが、これはcommonモデルの方を引き継いだのでしょうかね。 該当箇所をよく読んでみると、列挙されたモデルの4つ目にinitializionモデルというのがあり、 「定義になるかどうか」を初期化子の有無だけで判断するものがあったようですね。 さらにその後には、 (訳) >規格で採用されたモデルは、strict ref/defモデルとinitializationモデルの特徴を組み合わせたものとなっている。 >(略) >しかし、外部定義として働くのは、「初期化」あるいは「記憶域クラス指定子のない特定の宣言」のいずれでもよい。 >このような混合アプローチが選択されたのは、さまざまな環境と既存の実装に可能な限り広範囲に適応するためである。 とあります。これを読む限り、 extern int i3 = 3; の挙動はinitializationモデルを引き継いだ可能性もありそうです。 同モデルの説明のところにはexternの扱いは明記されていないので、少し話がずれているかもしれませんが。 有用な資料の箇所を指し示してくださってありがとうございました。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1054] Re:externと「外部結合」
投稿者:774RR
2007/09/03 15:52:21

C/C++ の予約語に関しては D&E で Stroustrup も書いているんだけど 「新しいキーワードを増やすことは激論を招く」pp.191 C/C++ では同じ予約語を文脈によって異なる意味に使うことで、増やさない選択をした。 違和感の原因はこの辺にありそう。 > 特に「紹介状」は使わせてもらっていいでしょうか。 どーぞご自由に。 <前橋様>すんません、勝手に盛り上がってしまっております。 本来こー言う[俺はこう思う]論は自分で blog 等立てるのが筋なんですが 小心者ですので web は「使う側」に回らせてもらっております。 もう少し掲示板スペースをお貸しください</前橋様>
[この投稿を含むスレッドを表示] [この投稿を削除]
[1053] Re:externと「外部結合」
投稿者:yuya
2007/09/03 14:29:41

▼ > 要するに言語仕様上の extern と、英語の単語 external の不一致に違和感がある > っつーことでっか そうです。すでに決まっちゃったものに文句を言っても仕方ないですが、 「説明するときに名前との関連を前面に出すか、抑えるか」という選択肢は残されています。 そもそも結合は定義サマが決めるのが筋なのであって、 定義でない宣言がいくら結合を声高に主張しても、 「定義との食い違いを処理系に見つけてもらおう」というのでない限り無意味ですよね。 int hoge = 1; static int hoge; でエラーを出してくれるならまだしも、未定義動作ですし。 それならば、「参照に(ほぼ)特化した、結合に関してはオールマイティな」 externのようなキーワードが用意されているのは合理的だと思いますが、 なにもそんな名前じゃなくても良かろう、と思うわけです。 # externは実は外部宣言(external declaration)から来ているのかも、というオチは……。 ▼ > プログラム言語のキーワードをいちいち英語とつき合わせていると違和感ありですな。 > 俺的に一番違和感があるのは const であります。 > const ではなく readonly にしてくれると **俺的には** 解消です。 私もまったく同じように思います。というか、 「文字通りではないことに気付いているかどうか」によって、 理解度が測れてしまうこと自体(Cはこんなのばっかり)、なんて不健全な状況なんだ(^^;) #define INTERN static #define READONLY const ... ▼レベル別教授法はとても参考になりました。 特に「紹介状」は使わせてもらっていいでしょうか。 「エキスパートCプログラミング」には「(定義でない宣言は)税関の申告のようなものだ」とあり、 初めて読んだときは???でしたが。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1052] Re:externと「外部結合」
投稿者:774RR
2007/09/03 11:26:05

ふむー 要するに言語仕様上の extern と、英語の単語 external の不一致に違和感がある っつーことでっか プログラム言語のキーワードをいちいち英語とつき合わせていると違和感ありですな。 俺的に一番違和感があるのは const であります。 const ではなく readonly にしてくれると **俺的には** 解消です。 言語規格書は古い非推奨な書き方してる既存のコードが invalid にならないように 過去との互換性維持のためだけの文言・機能も含んでいるので、そーいうところは 初心者・中級者むけクラスでは一切教えない。教える必要も無い。隠しておくべし。 たとえばいまさら「関数原型宣言にならない関数宣言」など教える必要は一切無い。 「extern があると先行する宣言に従う」はまさにこの黒歴史の部分。初心者に教えるな! # 知っておいて隠しておくのと知らないままでいるのとでは大きく違うのだがね。 extern に関して俺が後輩諸氏に説明するならばレベルに応じ 初心者向け(厳密に宣言を必ず先行させる新しいスタイルのみ教える) ・ヘッダファイル中において変数を宣言する場合には必ず extern を書くべし  →宣言とは他人/自分が [その名前] を使えるようにするものである  →他人の作った未知の関数や変数は使いたくないだろ。だから最低限素性がわからないと困るんだ   だから、ヘッダファイル中には[名前]と「紹介状」を書くべし(=これが宣言) ・他人/自分がその変数を使えるようにするために、定義を1つ .c 中に書くべし  →定義とは [その名前] の変数や関数を作って提供することである  →「紹介状」と実際の人物像が異なると困るだろ、だから紹介状を書く側には責任がある   人間の目でなくコンパイラでチェックできるよう、自作ヘッダは自作ソース中で #include しろ   そーすれば機械が不一致を検出してくれる  →紹介状は複数人で回覧することができる (宣言は複数回あっていい) が、   実際の人物は1人しかいないわけだから定義は1つ。 宣言と定義の簡易解説は必要。詳細解説までは不要。 結合や記憶域期間や可視性、等の詳細については説明不要 (消化不良になるだけ) 中級者向け(旧式の非推奨な書き方してるソースコードには触らせない) ・宣言と定義の詳細解説 ・内部結合と外部結合と無結合の違い  →「原則として」と前置きして extern は外部結合・定義にならない宣言となると解説  (変数宣言に extern があっても定義になりうると話しても混乱を招くだけなので説明しない)  →「原則以外は?」と問われたら上級者向け解説を実施 関数の場合の話を問われたら ・関数宣言と関数定義はソースコード上の記述形式が明確に異なるので区別できるから  extern/static は結合の決定にのみ使われるとフォローし ・static が明記されていない場合は extern が省略されていると解説 (X3010:1993 に準拠) ・これは変数の場合と挙動が異なるぞ、ともフォロー 上級者向け(既存のソースコードのダークサイドまで全部面倒見る人向け) ・言語規格書の章番号+解説 ・相手に興味がありそうで時間もあれば既に書いた俺妄想による Rationale をフォロー
[この投稿を含むスレッドを表示] [この投稿を削除]
[1051] Re:externと「外部結合」
投稿者:yuya
2007/09/02 16:34:38

774RRさん、(ぱ)さん、ありがとうございます。 たくさんコメントをいただいたので、[1045]~[1050]へのリプライは こちらにまとめさせていただきます。 [1048]774RRさん; > ながながと書いたけど結局のところ yuya さんの疑問点は > extern 記憶域指定子はなぜに (6.2.2) のようなヘンな振る舞いをするのか、その根拠やいかに > ということでよろしいのだろうか? [1049](ぱ)さん; >>static int i2 = 2; // 定義,内部結合 >>extern int i2 ; // 内部結合をもつ前の定義を参照する > > この「extern int i2;」の挙動が不可解だということですよね。 > 私にも不可解に見えます。そもそもキーワードの名前が「extern(al)」なのに! 6.2.2のexternの振る舞いが奇妙に映るのは、(ぱ)さんのおっしゃるとおり、 `extern'という、外部結合(external linkage)を想起させる名前によるところが大きいと思います。 いったん名前のことを忘れて、単に 「(翻訳単位内外にかかわらず)どこかで定義されている(かもしれない)変数・関数を参照する」 という機能が主眼だと考えれば、6.2.2の結合の定まり方は、 「可能な限り状況に合わせて結合を決定する」という、ある意味ではきわめて合理的な、 悪く言えば御都合主義的な仕様になっています。 その背景には[1048]・[1050]で774RRさんが書いてくださったような事情があった可能性が高いと思います。 現在の目から見て、初心者に「結局のところexternって何をするんですか?」と聞かれたとき、 最も正しい答えは[1046]でまとめていただいたように規格書の該当箇所を指し示すことだと思いますし、 最終的には本人にそこまで行き着いてもらわないといけないわけですが、 やはり(自分自身の頭を整理するためにも)「何が原則で、何が例外か」という観点で整理したくなり、 できるだけ普遍的な原則・少ない例外で理解できる方法はないだろうか、と思ってしまうわけです。 その過程の中で、歴史的経緯を知ることが原則を抽出する助けになることも多いと思います。 もちろん規格書には「これが原則、これが例外」などということは書かれていませんから、 注意しないと私が陥ったように勝手な行間解釈や俺俺用語を生んでしまうわけですが……。 で、実際にexternに対してそのような整理を試みたとき、ポイントとなる切り口は [1047]でご指摘のとおり「結合は何か」「定義になるかどうか」です。 ここでexternという名前を尊重して、「結合指定」を原則に据えると、 内部結合になるケースを例外と捉え、「定義になるかどうか」は別途論じることになります。 これに対して、「定義にならない」ことを原則に据えると、 (a); 結合は上記のように状況に合わせて定まる。 (b); 何も付けなくても初めから外部結合のブロック外定義になっているもの (関数定義や初期化子つきのブロック外変数定義)だけは、externをつけても定義とみなされる。 extern int hoge = 1; /* 外部結合のブロック外定義 */ と整理することができ、(b)が昔はコンパイルエラーになっていたとなれば、 例外として扱うことへの抵抗が小さくなります。 結局のところ、Cの振る舞いを統一的に理解することなど追求すべきでないのかも知れず、 [1045]774RRさんの > なんか難しく考えすぎなのではないかと思ってきた・・・ が最も正しいような気がしてきました。自覚はあるんですけど(^^;)
[この投稿を含むスレッドを表示] [この投稿を削除]
[1050] Re:externと「外部結合」
投稿者:774RR
2007/09/02 08:31:11

>最初期のCではexternはあってもなくても同じだったらしい 「最初期」をどのくらいまで遡るか次第、だけど 俺妄想だと Kerningham/Ritchie が作った直後のCには、たぶん extern はまったく無かったのだろうと思う。 なおかつリンカーが bss combine を勝手に行ってしまう都合上 ODR は実装したくても実装するすべが無かった。 ----aaa.c---- static int i1; // internal linkage, definition int i2; // external linkage, definition ----bbb.c---- static int i1; // internal linkage, definition, another instance of aaa.c!i1 double i2; // external linkage, definition, different decl of aaa.c!i2 ----bss in a.out--- aaa.c!i1; bbb.c!i1; common!i2; // combined together, uses maximum size bss combine 機能が無いリンカーをもつ処理系に移植する際に困ったので 後付で extern を追加したが、そのときにはすでに extern を持たないCで書かれた ソースコードがたくさんあったので、互換性を維持するには?と考えた結果が今の仕様 あと初期化子の有無で挙動が異なるのも、リンカーの都合 (非0の)初期化子がある=bss に置けない=bss combine が効かない って、これも妄想 歴史家なら、黒歴史を追及するのもまあお仕事ですし、実際面白そうだけど 1ユーザとしては今ある仕様を理解するほうが建設的かなーって思うのであった
[この投稿を含むスレッドを表示] [この投稿を削除]
[1049] Re:externと「外部結合」
投稿者:(ぱ)こと管理人
2007/09/02 00:26:07

えー、一応管理人としては何か言うべきところなんでしょうが、正直、 私にはわかりません。 >6.9.2 外部オブジェクト定義 例1(抜粋) > >static int i2 = 2; // 定義,内部結合 > int i2 ; // 前に内部結合をもつ定義があるため、 > // 結合の不一致が生じ、6.2.2によって > // 動作は未定義となる >extern int i2 ; // 内部結合をもつ前の定義を参照する この「extern int i2;」の挙動が不可解だということですよね。 私にも不可解に見えます。そもそもキーワードの名前が「extern(al)」なのに! Rationaleを見ても、このへんに関する記述は以下の箇所くらいのようです。 http://www.lysator.liu.se/c/rat/c1.html#3-1-2-2 これはこれで興味深いのですが(最初期のCではexternはあってもなくても同じだった らしい)、yuyaさんの疑問の回答にはならないように思います。 Relaxed Ref/Defモデルのところには、 | The appearance of the keyword extern (whether it is used outside of the | scope of a function or not) in a declaration indicates a pure reference | (ref), which does not define storage. (超訳) >externはどっかの定義の参照を意味し、記憶領域の定義ではない。 とあるので、そのモデル(UNIX Cのモデル)を引き継ぐとそういう考え方になるのかなあ、 と思いましたが… UNIX Cにおいてexternが内部結合の定義を参照するような 実装があったのでしょうか。 externは定義にはならない、と考えると extern int i3 = 3; これも不可解ですが、これはcommonモデルの方を引き継いだのでしょうかね。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1048] Re:externと「外部結合」
投稿者:774RR
2007/09/01 23:18:11

ながながと書いたけど結局のところ yuya さんの疑問点は extern 記憶域指定子はなぜに (6.2.2) のようなヘンな振る舞いをするのか、その根拠やいかに ということでよろしいのだろうか? 根拠 (Rationale) は規格書採択の際の委員会議事録などをさがせば出てくるんだろう。 でも俺は規格策定委員でもなんでもないただの1ユーザなのですだ。 そういう議事録みたいなのが公開されていれば誰か探してみてほしい。 まずは検証可能な事実を指摘: この「結合」の振る舞いは JIS X3010:1993 (ISO/IEC 9899:1990) の時点から ほとんど変わっていない。(一部些細な変更があるが今の議論には関係ないところ) 言語規格書ってのは、理想を追求するあまりに今あるユーザー・処理系を無視する わけにはいかない代物なわけだ。 したがって当時1990年頃に書かれていた「一般的なソースコードがすべて不適合」に なるような文言てのは採用するわけには行かない。理想と現実の落としどころが重要になる。 以下は俺の妄想: ヘッダファイルを多重に #include していて include-guard がうまく効いていない、 あるいは、同一大域変数の宣言を複数の別ヘッダファイルが行っている、 そんなソースコードでできたプログラムがあるとして。 #include の結果として、 翻訳単位中に extern の無い大域変数宣言 (要するに外部定義) が先に現れ、 後から extern のある大域変数宣言 (いわゆる変数宣言) が現れる、ような場合であっても うまいことコンパイル&リンクができるようにする必要があった。 すなわち extern 記憶域指定子の振る舞いは JIS X3010:1993 なら 6.1.2.2 JIS X3010:2003 なら 6.2.2 のように定めると都合がよかった。 そーいう話だと思って勝手にコメントしてみたけど、 いや、ずれてる!ということならまたよろしく。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1047] Re:externと「外部結合」
投稿者:774RR
2007/09/01 22:50:45

> そもそもリンケージが翻訳単位外に及ぶ(つまり外部結合)、というのと、 > 宣言が定義を探しに行く範囲が翻訳単位外に及ぶ(extern宣言)、というのは > 分けて考えるべきだと思うのですが、皆様のご意見はいかがでしょうか? この文章の意味が俺には解釈が難しいんだけど、つまるところ規格書が主張してるのは ・extern があれば外部結合になる (6.2.2) ・extern があっても外部結合であるとは限らない(6.2.2) ・extern があれば外部定義にならない(6.7+6.9.2) ・extern があっても外部定義になりうる場合がある(6.9.2) ・プログラム全体の中で現れる、外部結合をもつ同一識別子は、  そのすべての宣言において、同じものを表す (6.2.2) ということ。それ以上でも以下でもなくて、勝手に行間を読んではいけない。 extern 宣言なんて用語は言語規格書には無いわけで、あえて言うなら 「宣言に extern 指定子がある場合」と呼ぶべき。そして、解釈すべきは 「その宣言が同時に定義になるか否か」「その宣言の持つ結合は何か」 だけ。余計なことを考えずに素直に規格書通りにプログラムを読むだけでいい。 俺俺用語を勝手に作ってはいかん。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1046] Re:externと「外部結合」
投稿者:774RR
2007/09/01 22:25:46

このコメントは yuya さんむけというわけではなくて、第三者読者様の参考分として <結合の規則> 6.2.2 識別子の結合 オブジェクトの宣言の場合 (関数には適用されない) ・記憶域クラス指定なし=外部結合 ・static 指定あり=内部結合 ・extern 指定あり  「すでに宣言があって、その宣言で外部結合または内部結合が指定されている」   →その既存の宣言と同じ結合をもつ  「すでに宣言があって、その宣言で無結合が指定されている」または「宣言が無い」   →外部結合 </結合の規則> <宣言> ・無結合の識別子の宣言が同一スコープ内に2つ以上あってはならない </宣言> ということは、外部結合の宣言が2つあってもよいということ。 6.9.2 の例 int i4; // 宣言、外部結合の仮定義 int i4; // 宣言、外部結合の仮定義 extern int i4; // 宣言、既出の結合を採用、この場合は外部結合 static int i5; // 宣言、内部結合の仮定義 extern int i5; // 宣言、既出の結合を採用、この場合は内部結合 6.9.2 に出てない例 (外部定義にならないのでわざと出していない) extern int i6; // 宣言、既出の宣言が無いので外部結合、外部定義にならない (*136) 脚注のとおり、識別子を使わなければ定義は無くてよい
[この投稿を含むスレッドを表示] [この投稿を削除]
[1045] Re:externと「外部結合」
投稿者:774RR
2007/09/01 22:03:00

なんか難しく考えすぎなのではないかと思ってきた・・・ 言語規格書は書いてあるとおりに読めばよくて、勝手に行間を解釈しては誤るよ。 <[外部定義かそうでないか]の解釈> JISX3010:2003 6.9.2 外部オブジェクト定義、で述べられている内容は要約すると ・オブジェクトの外部定義=ファイル有効範囲と初期化子をもつ場合 ・初期化子がなく、記憶域クラスなしか static があるとき=仮定義 ・仮定義が1つ以上あって外部定義が無い場合は、翻訳単位の終了時点で  仮定義を =0 という初期化子を持つ外部定義と読み替える。 以上。 よってここに書いてないものは外部定義ではないっつーことだ。 記憶域クラス指定 extern をもつ宣言についてはここ 6.9.2 では記載が無いので、 外部オブジェクト定義ではない。 > 私は定義ではなく、ただの宣言である。 ここまでは正しい。というか「外部定義にならない宣言である」というべきかな。 > 定義はほかにある そんな勝手な行間を読んではいけない。これに関しては別に規定がきっちりある。 6.9 外部定義、において ・外部結合で宣言した識別子を使用している場合、プログラム全体の中でその識別子に  対する外部定義がちょうど1つなければならない。 ・使用していない場合、1つ以下でなければならない (*136) (*136) 外部結合で宣言した識別子を使用しない場合、その外部定義は必要ない </[外部定義かそうでないか]の解釈>
[この投稿を含むスレッドを表示] [この投稿を削除]
[1044] Re:externと「外部結合」
投稿者:yuya
2007/08/31 10:38:45

774RRさん、ありがとうございます。 0. C限定の話です。 1. はい、ご紹介くださったものと同じものを入手しています(保存も……)。 2. 変数のextern宣言について[1043]で成り立つことは、 関数原型宣言(externをつけてもつけなくても同じですが)についても成り立つと思うので、関数も含む話です。 3. 処理系準拠限定の話です。[1043]の末尾の非準拠の話は、歴史的経緯の手がかりになるかも、と思って付け加えました。 で、この議論に関係する条項は JISX3010:2003 6.2.2 識別子の結合 6.9 外部定義(とくに 6.9.2 外部オブジェクト定義) になると思います。 [1043]の投稿は、[1025]に774RRさんが書かれた > extern の意味するところは[1020]で書いたとおり、結合指定 > (略) > 厳密なところは自分で JIS 言語規格書を参照してもらうとして、簡単に言うなら > extern:外部結合を指定、static:内部結合を指定 というところを読んで、その意味を考えたのがきっかけです。 確かに6.2.2にはextern宣言された識別子の結合が場合分けされて書かれていますが、 よく読むと回りの定義状況に合わせた定まり方になっており、私には(ごく自然な) *結果* と映ります。 だとすると、extern宣言はどんな「結合指定」を担っているのだろう?と疑問に思ったのです。 特に、初期化子のない外部(ブロック外)変数定義・宣言の場合、 externをつけないほうがむしろ積極的に「外部結合」を指定しているように思います。 内容は前回の繰り返しになりますが、規格書で言えば 6.9.2 外部オブジェクト定義 例1(抜粋) static int i2 = 2; // 定義,内部結合 int i2 ; // 前に内部結合をもつ定義があるため、 // 結合の不一致が生じ、6.2.2によって // 動作は未定義となる extern int i2 ; // 内部結合をもつ前の定義を参照する が該当します。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1043] Re:externと「外部結合」
投稿者:774RR
2007/08/30 20:56:28

詳しい議論(重箱の隅ともいう)に入る前に確認事項をいくつか 0.この話は C 限定か?それとも C++ 限定か? 1.言語規格書は読みましたか?今手元にありますか? (C89 なら JIS X3010:1993 ないし ISO/IEC 9899:1990) (C99 なら JIS X3010:2003 ないし ISO/IEC 9899:1999) (C++ なら JIS X3014:2003 ないし ISO/IEC 14882:2003) 2.変数の話限定か?それとも関数も含むか? 3.規格非準拠の処理系の話も含むか?規格書採択前を含むか? 規格非準拠の処理系の話ならパスします(きりがないので) C/C++ 言語規格書が無いのであればお話にならないです。げっちゅしてください。 http://www.jisc.go.jp/ から*閲覧*が可能です (小細工すれば保存もゲフゲフ) 検索が効かないので役に立ちませんが、無いよりは無限大にマシです。 んで、疑問に思われる/わからないところを具体的に条項番号を挙げて話してください。
[この投稿を含むスレッドを表示] [この投稿を削除]
[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以前はコンパイルエラーになっていたと聞いたことがあります。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1041] 管理者により削除されました
2007/08/30 03:03:40

引用のみの投稿だったため削除しました。
[この投稿を含むスレッドを表示]
[1040] 管理者により削除されました
2007/08/30 03:04:15

引用のみの投稿だったため削除しました。 …ていうか何の嫌がらせよ。
[この投稿を含むスレッドを表示]
[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)を問題視したプログラマがこの小細工を使った」 の意図です。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1038] Re:externについて
投稿者:yuya
2007/08/28 07:16:42

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

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

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

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

引用のみの投稿だったため削除しました。
[この投稿を含むスレッドを表示]
[1031] Re:自動変数を返すこと
投稿者:ひげおやじ
2007/08/25 12:53:42

ご回答ありがとうございますm(_ _)m >returnで変数の値を返すときは、それがポインタかどうかに関わらず、別の場所へ中身がコピーされます。 この「returnの動作」の理解が足りませんでした。おかげさまでモヤモヤが取れました。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1030] Re:externについて
投稿者:yuya
2007/08/25 08:46:08

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

日本語がまずかったので訂正。 >呼び出し側で返されたポインタを使って、自動変数(のあった所)にアクセスしているからまずいのです。 呼び出した関数から返されたポインタを使って、自動変数(のあった所)にアクセスしているからまずいのです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1028] Re:自動変数を返すこと
投稿者:トル
2007/08/25 01:46:46

はじめまして。 >そこに載っている関数int_to_strではポインタを返しているからまずいの >でしょうか?それとも、ポインタでなく整数値を返す場合でも、関数の >中で宣言した自動変数の値を返すことはやめた方がよいのでしょうか。 呼び出し側で返されたポインタを使って、自動変数(のあった所)にアクセスしているからまずいのです。 >たとえば、次のような関数は(int_to_strと同じ観点から見て)まずいのでしょうか。 > >int keep(int int_value) >{ > int i; > i = int_value * int_value; > return i; >} この場合は問題ありません。 returnで変数の値を返すときは、それがポインタかどうかに関わらず、別の場所へ中身がコピーされます。 自動変数のポインタを返すとまずいのは、そのポインタが指す領域がなくなってしまうからです。 int *f(void) { int a = 0; return &a; } と言う関数があったとき、 int *p = f(); printf("%p", p); /* pの値を表示する */ のようにすることは何の問題もありません。しかし、ポインタが指している領域はすでに開放されているので、 int *p = f(); printf("%d", *p); /* pが指している領域の値を表示する */ のような事はできない、と言うことです。 こんな感じでしょうか?
[この投稿を含むスレッドを表示] [この投稿を削除]
[1027] 自動変数を返すこと
投稿者:ひげおやじ
2007/08/24 23:24:01

お世話になっております。 「C言語ポインタ完全制覇」の97ページの補足「自動変数の領域は、 関数を抜けたら開放される!」のつながりで質問させてください。 そこに載っている関数int_to_strではポインタを返しているからまずいの でしょうか?それとも、ポインタでなく整数値を返す場合でも、関数の 中で宣言した自動変数の値を返すことはやめた方がよいのでしょうか。 たとえば、次のような関数は(int_to_strと同じ観点から見て)まずいのでしょうか。 int keep(int int_value) { int i; i = int_value * int_value; return i; } ご返答いただけると幸いです。 (なにぶん継ぎ接ぎの知識なもんで、この質問自体がまずいかもしれませんが・・・)
[この投稿を含むスレッドを表示] [この投稿を削除]