BABYL OPTIONS: -*- rmail -*- Version: 5 Labels: Note: This is the header of an rmail file. Note: If you are seeing it in rmail, Note: it means the file has no messages in it.  0, unseen,, *** EOOH *** Newsgroups: fj.archives.answers,fj.lang.c Path: newsmaster.tuis.ac.jp!news.chiba-u.ac.jp!chiba-ns!newssinet!hakata!aero.kyushu-u!kuee-news!tamaru-news!Q.T.Honey!news01.so-net.or.jp!news01.sinfony.ad.jp!news-jp-0.abone.net!np0.iij.ad.jp!nspixp!wnoc-tyo-news!ykgw!xeroc!leia!133.140.40.2!kitano From: kitano@crd.yokogawa.co.jp (Kinichi - Kinchan - Kitano) Subject: comp.lang.c Answers to Frequently Asked Questions (Abridged) in Japanese Sender: news@leia.pa.yokogawa.co.jp (Leia news server) Message-ID: Supersedes: Date: Tue, 9 Sep 1997 09:01:55 GMT Expires: Fri, 31 Dec 1999 12:00:00 GMT Reply-To: kitano@crd.yokogawa.co.jp Organization: Yokogawa Electric Corporation, Tokyo, Japan. Followup-To: fj.lang.c Lines: 1915 Xref: newsmaster.tuis.ac.jp fj.archives.answers:1325 fj.lang.c:6061 Archive-name: c-faq-abridged Last-modified: 9 August 1997 C FAQ簡易版 [1997年9月8日変更 北野 欽一] (訳注:1996年9月5日版に基づく (頭に[Last modified September 5, 1996 by scs.]とあるもの)) この記事の著作権は1990年から1996年に渡ってSteve Summitに帰属する。書籍 『C Programming FAQs: Frequently Asked Questions』の内容を著者と出版社 の許可の元、社会への貢献のために使用している。このFAQは書籍版を補足す ることを意図としている。内容は各国の著作権法によって守られる。個人使用 にかぎっては自由に参照してよい。ただし許可なく再出版することは許さない。 (訳注:日本語版の著作権は北野欽一に帰属する。) * * * この記事はcomp.lang.cでよく聞かれる質問とそうした質問に対する最小限の 回答を集めたものである。これより詳しい説明や他の情報への参照は長篇FAQ (どうやって手に入れるかは質問20.40を参照すること。rtfm.mit.eduからftp で、あるいはmail-server@rtfm.mit.eduというメッセージ「help」を付けてメー ルを送ることでも入手することができる。Webバージョンは http://www.eskimo.com/~scs/C-faq/top.htmlで見ることができる。あるいは 書籍 C Programming FAQs: Frequently Asked Questions_ (Addison-Wesley, 1996, ISBN 0-201-84519-9)を参照して欲しい。(訳注: 日本語版はトッパンよ り『CプログラミングFAQ Cプログラミングのよく尋ねられる質問』(ISBN 4-8101-8097-2)として出版されている。) 1章. 宣言と初期化 1.1: どの整数型を使えばいいか、どうやって決めればいいか。 A: 大きな値(何万という大きさ)が必要ならlongを使う。そうではなく小 さい領域で済ますことが大事ならshortを使う。そうでなければintを 使う。 1.4: 最近登場した64ビットマシンの、64ビット整数のデータ型は何である べきか。 A: 諸説あるが、long intやlong long intがいいという意見が有力か。 1.7: 外部変数を宣言、定義する一番いい方法は。 A: 一番よい並べ方は、まず各定義を関連する.cファイルに入れて、 外部宣言はヘッダファイルに入れる。 1.11: 関数宣言についたexternは何を意味するのか。 A: 実は何の意味もない。 1.12: autoというキーワードは何の役に立つのか。 A: 何の役にも立たない。 1.14: 自分へのポインタを含んだリンク付きリストのノードをうまく定義す ることができない。 A: Cの構造体は、自身へのポインタを要素として持つことができる。K&R の6.5章の議論と例が、この点を明らかにしてくれる。こうした定義 の途中でtypedefを定義(して使用)しようとすると問題が起こる。こ うしたtypedefの定義は避けよう。 1.21: charへのポインタを返す関数へのポインタを返す関数へのポインタN 個からなる配列をどうやって宣言すればいいか。 A: char *(*(*a[N])())(); typedefを使って宣言を段階的に作り出すか、cdeclというプログラムを 使うことで、こうした複雑な宣言を作り出すのが容易になる。 1.22: 自分と同じ型を持つ関数へのポインタを返すことのできる関数をどう やって宣言すればいいか。 A: 直接に定義することはできない。キャストを使うか、ポインタに構造 体をかぶせて構造体を返すようにする。 1.25: 私が使っているコンパイラは関数の無効な再宣言だと文句を付ける。 一度定義しているだけなのに。 A: 未宣言の関数を呼び出すことは、その関数がintを返すと暗黙に宣言す ることになる。 1.30: 明示的には初期化されていない変数の初期値について、どこまで安心 して仮定することができるか。 A: "静的な"寿命を持つ変数は、プログラマが初期化したみたいに、0と して生まれる。"自動の(automatic)"寿命を持つ変数の中身は、ゴミ である(calloc()を使った場合を除く)。 1.31: どうしてローカルの配列を文字列で初期化することができないのか。 A: たぶん、君の使っているコンパイラはANSI規格ができる前のものだか らだろう。 1.31a: char *p = malloc(10);のどこがまずいのか。 A: 関数呼び出しは、静的変数やローカルでない変数の初期化子としては 許されていない。 1.32: 以下の二つの初期化の違いは。 char a[] = "string literal"; char *p = "string literal"; A: 最初のは値を書き換え可能な初期化済みの配列を宣言する。2番目の は書き換え可能とは限らない文字列定数で初期化されたポインタを宣 言する。 1.34: 関数へのポインタをどうやって初期化すればいいか。 A: extern int func(); int (*fp)() = func;のようにすればいい。 2章 構造体、共用体、列挙体 2.1: 以下の二つの宣言の違いは。 struct x1 { ... }; typedef struct { ... } x2; A: 最初の構造体は構造体タグで名指しされる。2番めの構造体はtypedef で名指しされる。 2.2: struct x { ... }; x thestruct; ではなぜ駄目なのか。 A: CはC++ではない。 2.3: 構造体は自身へのポインタをメンバとして含んでいてもいいのか。 A: 質問1.14を参照のこと。 2.4: Cであいまいな(抽象)データ型を実装する一番よい手は。 A: よいやり方の1つは、定義してあることを公開していない構造体デー タ型を指すポインタを使うことである。 2.6: 最後のメンバが要素1つの配列である構造体を宣言して、巧妙にメモ リ割り付けをして、配列が複数の要素を持つように振る舞わせている コードを見た。このコードは許されるのか。移植性は高いのか。 A: 公式な解釈によると上の技巧は、Cの規格に厳密には準拠していない と考えられる。 2.7: 構造体を、変数に代入することも、関数に引数として渡すことも、関 数の戻り値としても使うこともできると聞いた。けれどK&R初版には できないと書いてある。 A: 現在のコンパイラはこれらすべてに対応している。 2.8: なぜ構造体を比較することはできないのか。 A: 構造体の比較を、ハードウエアに近い部分の操作も可能というC言語 の特徴を殺さず、コンパイラにやらせる単純明解な方法はないからだ。 2.9: どんな仕組みで構造体を引数で渡したり、関数の戻り値に使うことが できるのようになっているのか。 A: 本当に知りたければ、省略なしのFAQを見ること。 2.10: 構造体を引数として取る関数に定数値をどうやって渡せばいいか。 A: できない。C言語には名前のない構造体の値を作り出す方法は存在し ない。 2.11: 構造体をデータファイルから読む、あるいはデータファイルに書き込 むのはどうすればいいか。 A: fread()やfwrite()を使うのはそんなに難しくない。 2.12: 構造体に詰め物が付かないようにするにはどうすればいいか。 A: 標準的な方法はない。 2.13: 構造体にsizeof演算子を使ったら、私が思っていたよりも大きな大き さを返してきた。 A: 構造体を配列にしたときにも境界合わせ(alignment)が正しくなるように 詰め物が付くからである。この詰め物の分だけ大きくなる。 2.14: 構造体内のフィールドのバイトオフセットを知る方法は。 A: ANSI Cは、offsetofマクロを用意しているので、手元の環境で使えれ ば場合は使うこと。 2.15: どうやれば構造体のフィールドを、実行時に名前でアクセスできるか。 A: offsetof()マクロを使って名前とオフセットの対応表を用意する。 2.18: 私の関数は正しい結果を出力するけれど終了した時点でcoreを吐く。 なぜか。 A: main()の直前で構造体の型宣言の最後のセミコロンが抜けてないか調 べよう。抜けていると、main()が構造体を返すと宣言していることにな る。質問10.9と16.4も参照すること。 2.20: 共用体を初期化することはできるか。 A: 共用体の最初のメンバに対して初期化を行うことをANSI C規格は許し ている。 2.22: 列挙体を使うことと#defineを複数使うことの違いは。 A: 現状ではほとんど違いはない。C規格は、列挙体とそのほかの整数型 を混在して使っても問題ないと述べている。 2.24: 列挙体の値を(値ではなく)シンボルで表示する楽な方法はないのか。 A: ない。 3章 式 3.1: なぜ a[i] = i++; はうまく動かないのか。 A: 変数iが同じ式の中で参照されかつ変更されているからだ。 3.2: 私が使っているコンパイラでは以下のコードで49を出力する。 int i = 7; printf("%d\n", i++ * i++); 評価の順にかかわりなく、56を出力するのではないのか。 A: 後置増分演算子++も後置減分演算子--も元の値を返した後で、かつ式 の終りより前のどこかでその演算を行う。しかし式のその他の部分が 評価される直後や直前とは限らない。 3.3: どうして int i = 3; i = i++; の結果が7になるようなことがあるの か。 A: 未定義の動作というのは、どんなことが起っても不思議がないという ことである。 3.4: 括弧をつけて評価の順をこっちの好きなようにすることができるか。 A: 演算子の優先順位や括弧を付けることは、式の評価に部分的に順序付 けをするだけである。この中には一般には副作用が発生する順序は含 まれていない。 3.5: それでは&&や||はどうなるのか。 A: これらの演算子は特例であって、左から右へ順に評価されることが保 証されている。 3.8: 「副作用完了点(sequence point)」とはなにか。 A: 副作用完了点とはすべての副作用が完了したことが保証されている (時)点である(つまり式の本当の終わり、||、&&、?:、,(カンマ演算子)、 関数呼び出しの直前)。 3.9: a[i] = i++; という式があったとして、a[]のどの要素に書きこまれ るかはわからないが、iは確実に1つ大きくなるね。 A: 違う。式やプログラムが未定義になったら、すべての面で未定義とな る。 3.12: 式の値を使わないとして、変数に1を加えるのにi++と++iのどちらを 使えばいいのか。 A: この2つは産み出す値が違うだけで、副作用だけを必要とするときは まったく同じである。 3.14: なぜ以下のコードは私が思った通りに動かないのか。 int a = 1000, b = 1000; long int c = a * b; A: オペランドのどれかを(long)でキャストしなければならない。 3.16: ?:を代入式の左辺に使うことができるか。 A: 使えない。 4章 ポインタ 4.2: char *p; *p = malloc(10); のどこがまずいのか。 A: 君が宣言したポインタはpであって*pではない。 4.3: *p++はpを増分するか。それともpが指すものを増分するのか。 A: *p++はpを増分する。pが指していた値を増分するには(*p)++を使う。 4.5: charへのポインタに、intを飛び越えて次を指してもらいたい。 なぜ ((int *)p)++; ではうまくいかないのか。 A: Cではキャスト演算子は変換演算子であって、それは右辺値を生みだ すと定義されている。右辺値であるから、代入することも++で足し算 することもできない。 4.8: ポインタを引数として取って、そのポインタを初期化することになっ ている関数がある。けれど呼んだ側のポインタの値は変わらなかった。 A: 呼ばれた側の関数は、ポインタの渡されたコピーを変更するだけであ る。 4.9: 参照呼び出しに使うため汎用のポインタを関数に渡すのに、void ** を使うことができるか。 A: 移植性まで考えれば不可能である。 4.10: intへのポインタを引数として取る関数がある。どうすれば5のような 定数を参照渡しすることができるか。 A: 一時変数を定義する必要がある。 4.11: Cにも"参照渡し"が用意されてるか。 A: まさか。けれど参照渡しを真似ることはできる。 4.12: 関数を呼ぶのに、ポインタを通す方法をみたことがある。 A: おまけの括弧とわざわざ*を付けることは公式には省略可能になって いる。ただし古い処理系はそれらを必要とする。 5章 ヌルポインタ 5.1: そもそもこの悪名高いヌルポインタとは何か。 A: どんなポインタの型にも特別な値、すなわち「ヌルポインタ」が存在 する。このヌルポインタは他のどんなポインタの値とも区別可能で、 どんなオブジェクトや関数へのポインタとも一致しない。 5.2: どうやればプログラムの中でヌルポインタを得ることができるのか。 A: ポインタを書くべきところに現れた定数0は、コンパイル時にヌルポ インタに変換される。ポインタを書くべきところとは、初期化や代入 や比較をするときに左辺/右辺のどちらかにポインタ型の変数か式が 現れたものや、(ANSI Cでは)関数引数として表れるポインタでスコー プ内にその関数のプロトタイプが存在する場合を指す。その他の状況 (プロトタイプのない関数や可変個数引数を取る関数の可変個数の引 数リストの可変部)では定数0に適切なキャストを付けることが必要に なる。 5.3: ポインタがヌルポインタでないかどうかのテストの省略形 if(p) は 有効なのか? A: 有効である。if(p) という構造はヌルポインタがどんな内部表現でも 有効である。コンパイラは必ず if(p != 0) と書かれたように実質的 に書き換え、0を正しいヌルポインタに変換する。 5.4: NULLとは何で、どう#defineされているのか? A: NULLは単なるマクロである。0( あるいは((void *)0) )と定義されて いる。これは(書き方の面から、キャストのない0を書くことを嫌って) ヌルポインタを生み出すのに使われる。 5.5: ヌルポインタの内部表現に0でないビットパターンを使っているマシ ンでは、NULLはどう定義するべきか。 A: ほかのどんなマシンとも同じである。0 (または((void *)0))と定義 されている。(0を見て変換を行うのはコンパイラの仕事であって プリプロセッサの仕事ではない。) 5.6: もしNULLが ((char *)0) と定義されていると、キャストされていな いNULLを引数として渡す関数呼び出しが動かなくなるのでは? A: 動かなくなる場合もある。ここで問題はデータの型が異なるとポイン タの内部表現が異なるマシンがあることである。それでも、コンパイ ラにどんなヌルポインタが必要か伝えるためにキャストが必要である。 なぜならそれは (char *)0 とは異なるからである。 5.9: もしヌルポインタを表わす数としてNULLと0が同じものを表すなら、 どちらを使えばいいのか。 A: どちらでもいい。違いはまったく書き方の上のものである。 5.10: でも0よりはNULLを使うほうが、NULLの値が将来変わることを考える と、とくにヌルポインタの内部表現が0でないマシンについては優れ ているのでは。 A: いや。NULLは定数0である。だから定数0も十分に用を足す。 5.12: 私は、データ型に応じたヌルポインタを作り出すのに以下のマクロ を使っている。 #define Nullptr(type) (type *)0 A: この技は、文法的に正しいけれど、たいして役には立たない。 5.13: 変だな。NULLは0となることが保証されている。けれどヌルポインタ は0となることは保証されていないね? A: "ヌルポインタ"とはC言語上の概念であって、特定のマシンが内部で どんな値を使ってようが関係ない。ヌルポインタはソース上では文字 0で要求される。NULLはプリプロセッサのマクロで、これは0と(ある いは ((void *)0) と)必ず#defineされる。 5.14: なぜヌルポインタに関する混乱が存在するのか。なぜこれらの問題が こんなに何度も出て来るのか。 A: ヌルポインタが、ソースコード上も多くマシンの内部表現上も0であ ることで、実際には保証されない仮定を信じ込む人がいるようだ。 NULLというマクロを使うことから、その値が将来変化するかもしれな いとか、妙なマシン上では0でないことを指しているように思えるの かもしれない。 5.15: 混乱している。このヌルポインタに関するごたごたが理解できない。 A: 簡単な規則にすると、「必ず0かNULLを使う。関数呼び出しの引数と して使われるときは必ずキャストする。」となる。 5.16: ヌルポインタを取り巻くこれらの混乱を考えれば、単純にヌルポイン タは内部では0で表現されると決めてしまったほうが簡単なのでは。 A: そのような決まりを作っても得られるものはもほとんどない。 5.17: ヌルポインタに0以外の値を使用するマシンや、異なる型のポイン タに異なる内部形式を持つマシンは本当に存在するのか。 A: Prime, Honeywell-Bull, CDCの各社のマシン、Symbolic社のLispマシ ンは非0のヌルポインタを使っている。 5.20: 実行時に出る「null pointer assignment(ヌルポインタによる代入)」 というエラーメッセージは何を意味するのか。 A: ヌルポインタを使って、無効な領域に書き込んでしまったことを意味 してる。(質問16.8も参考。) 6章 配列とポインタ 6.1: あるソースファイルでchar a[6]と定義して、別のファイルで extern char *aと宣言した、なぜこれはうまくいかないのか。 A: extern char *aという宣言が、実際の定義と食い違うからである。 extern char a[]を使おう。 6.2: でもchar a[]はchar *aと同じと聞いたことがあるが。 A: 全然別のものである。配列はポインタと違う。x[3]というふうに参照 することは、xが配列かポインタかで違ったコードが生成される。 6.3: Cで"ポインタと配列は同等"というのは何を意味しているのか。 A: 式中に現われる型「Tの配列」という左辺値は、配列の最初の要素を 指すポインタに意味が格下げになる。結果としてできるポインタの型 は「Tへのポインタ」となる。したがって配列aとポインタpがあった とすると、p = a;と書くことは可能である。こうするとp[3]とa[3]は 同じ要素を指す。 6.4: それではなぜ関数の仮引数では配列とポインタの宣言が交換できるの か。 A: そのほうが便利だからだと考えられているからである。 6.7: どうして配列は代入できないのに左辺値(lvalue)なのか。 A: 配列は「変更可能な左辺値(modifiable lvalue)」ではない。 6.8: 実際のところ、配列とポインタの違いは。 A: 配列は自動的に領域を割り付ける。これは大きさも割り付けられる場 所も固定である。ポインタは動的で、指す先の大きさも指す先も実行 中に変更することができる。 6.9: 配列とは定数ポインタにすぎないと説明してくれた人がいた。 A: 配列の名前は、名前には代入できないという点で"定数"であるが、配 列はポインタでは断じてない。 6.11: 5["abcdef"] という式を含んだジョークのコードを見たことがある。 どうしてこの式がC言語で文法上正しいことになるのか。 A: 配列の添字演算子[]の二つのオペランドは交換可能である。配列の添 字付けa[e]は *((a)+(e)) と同じものであるという定義になっている。 6.12: arrayと&arrayの違いは。 A: データ型が違う。 6.13: 配列へのポインタをどうやって宣言するのか。 A: たいていは、そんなポインタを宣言したいのではない。配列の要素へ のポインタを使うことを考えよう。 6.14: どうすれば配列の大きさをコンパイル時に設定することができるか。 A: malloc()とポインタを使えば難しくない。 6.15: 引数として渡された配列の大きさに合ったローカルな配列をどうやっ て宣言することができるか。 A: できない。配列の大きさはコンパイル時に定数でなければならない。 6.16: 多次元の配列を動的に割り付けるのはどうしたらいいか A: たいていポインタの配列を割り付けて、それぞれのポインタを動的に 割り付けた"列"に初期化するのが一番の解決策である。コードの例は 長篇FAQを参照してほしい。 6.17: 0始まりでない配列を、ポインタを使って真似ることができるか。 A: アクセスされることを想定されたメモリのブロック以外をポインタが 指すことがあれば不可能だ。 6.18: 私が使っているコンパイラは、ポインタへのポインタを使うべきとこ ろで2次元配列を使うと不満をいう。 A: 配列がポインタに成り下がるという規則は、再帰的には適用されない。 配列の配列(例えばC言語における2次元配列)は、配列へのポインタに 成り下がるのであって、ポインタへのポインタに成り下がるわけでは ない。 6.19: コンパイル時に"幅"が未定の2次元配列を引数とする関数はどうやっ て書けばいいか。 A: これは結構難しい。 6.20 関数の引数として配列を渡すときに、静的に割り付けた配列も動的に 割り付けた配列も受け付けるようにするにはどうしたらいいか。 A: 唯一の完全な解というのは存在しない。ヒントが欲しければ長篇FAQを 参照のこと。 6.21: なぜサブルーチンの引数として渡された配列の大きさをsizeof()で き ちんと計算できないのか。 A: コンパイラから返ってくるのは引数であるポインタの大きさである。 関数が実際に受けとるのは(配列ではなく)ポインタだからだ。 7章 メモリの割り付け 7.1: どうして char *answer; gets(answer); ではうまくいかないのか。 A: ポインタ変数answerは有効な領域を指していない。 上のプログラムを修正する一番やさしい方法は、ポインタの代わりに ローカルの配列を使うことだ。 7.2: strcat()がうまく動かない。char *s3 = strcat(s1, s2); で変な答 えが返ってきた。 A: ここでも一番の問題は連結した結果を貯える領域がうまく割り付けら れていないことである。 7.3: けれどstrcat()のmanページによると、strcat()は引数として2つの charへのポインタを取ることになっている。領域の割り付けが必要で あるとどうやって知ることができるのか。 A: ポインタを使うときは一般に、いつも領域の割り付けのことを考えて おかなければならない。少なくともコンパイラが割り付けを代わりに やってくれると明記してあることを確認しておかなければならない。 7.5: 文字列を返すはずの関数がある。けれど呼んだ側の関数に返ってくる と、返ってきた文字列にはゴミが入っている。 A: 関数が返す文字列を格納する領域が正しく割り付けられている(すな わち、ローカルに割り付けられたものではない)ことを確かめること。 7.6: どうしてmalloc()を呼ぶと「警告: 整数をポインタに代入の際にはキャ ストが必要」というのが出るのか。 A: を#includeしたか? 7.7: mallocが返した値を割り付けたデータ型のポインタに注意深くキャス トしているコードをたまに見るのはなぜか。 A: ANSI/ISO規格が成立するまでは、ある種の警告を黙らせるの必要であっ た。 7.8: どうしてこんなに多くのコードが、文字列を割り付けるときに sizeof(char)を掛け算するのを忘れているのか。 A: sizeof(char)は定義によりぴったり1であるから。 7.14: オペレーティングシステムによってはmallocしたメモリを実際に確 保するのをプログラムがそのメモリを使おうとするまで先延ばしす ると聞いたことがある。これは文法上許されるのか。 A: これは答えにくい。 7.16: 数値演算をするのに、大きな配列を割り付けようと考えている。 けれどmalloc()の動きが変だ。 A: malloc()に渡そうとしている数がsize_tが保持できる数を越えないよ うにする。 7.17: 私のPCには8MBもメモリが載っている。どうして640Kかそこらしか malloc()できなさそうなのか。 A: PC互換機のセグメント付きのアーキテクチャーでは、640Kより多くの メモリを見通しよく使うことは非常に難しい。質問19.23も参照のこと。 7.19: プログラムがコケる。malloc()の内部のどこかであることは間違いな いと思うんだが。 A: mallocしたのより大きい領域を使っていないか。文字列を扱っている ときは要注意である( strlen(str) + 1 バイト必要である)。 7.20: 動的に割り付けた記憶領域は解放した後には使えないね。 A: 使えない。昔の解説には違うことが書いてあるものがあったが、 これはもう全く正しくない。 7.21: どうしてポインタがfree()を呼んだ後でヌルポインタにならないのか。 A: Cの値渡しとは、呼ばれた側の関数が自分の引数を決して変えることは ないということだからである。 7.22: ローカルなポインタ用にメモリをmalloc()で割り付ける。わざわざ free()を呼ばなければならないか。 A: もちろん。 7.23: 動的に割り付けたオブジェクトへのポインタを含む構造体を割り付け ている。構造体を解放する前に、構造体が含むポインタの先のオブジェ クトを全部解放しなければならないのか。 A: そのとおり。 7.24: プログラムが終了する前に、割り付けたメモリを解放しなければならな いか。 A: その必要はない。 7.25: どうしてfreeしてもメモリの使用状況が変わらないのか。 A: たいていのmalloc/freeの実装は、解放されたメモリをオペレーティ ングシステムに返さない。 7.26: free()は、何バイト解放するかをどうやって知るのか。 A: malloc/freeの実装は、割り付けたメモリブロックそれぞれと、 解放されたメモリブロックそれぞれの大きさを記憶しているからだ。 7.27: だったら割り付けた領域の大きさをmallocパッケージに聞くことがで きるのか。 A: 移植性の高い方法では不可能である。 7.30: realloc()の第1引数にヌルポインタを使うことは許されているのか。 A: ANSI Cはこの使用方法を許している。けれども昔のコンパイラには対 応していないものもある。 7.31: calloc()とmalloc()の違いは。 A: calloc()は引数を2つ取る。そして割り付けた領域を全ビット0にする。 7.32: alloca()とは何者で、なぜ使わないほうがいいと人は言うのか。 A: alloca()は領域を割り付け、その領域はalloca()を起動した関数を抜 けた時点で自動的に解放される。alloca()を移植性が高いように書く ことはできないし、スタックのないマシン上に実装することは難しい。 それに安直な実装では、うまくいかない状況がある。 8章 文字と文字列 8.1: どうして、strcat(string, '!'); はうまく動かないのか。 A: strcat()は文字列を連結する。文字を連結するわけではない。 8.2: どうして if(string == "value") で文字列と値を正しく比較するこ とができないのか。 A: 上の式はポインタとポインタの比較をしている。文字列と文字列を比 較するにはstrcmp()を使う。 8.3: どうして文字列を文字の配列に代入することができないのか。 A: 文字列は配列で、配列に直接は代入できない。代わりにstrcpy()を使 おう。 8.6: 文字に対応する数値(文字集合)の値をどうやって求めればいいか。 A: Cでは、文字を持っているということは、すでにその値を持っている ということである。 8.9: どうしてsizeof('a')の値が1ではないのか。 A: Cの文字定数はint型を持つ。 9章 ブール数 9.1: ブール値をC言語で扱うのに適切なデータ型は? A: 絶対的な回答はない。ブール型の選択にまつわる議論については長篇 FAQを参照のこと。 9.2: 組み込みのブール値を返す演算子や関係演算子が1以外の値を"返した ら"どうなるか。 A: 組み込みの演算子によってブール値が産み出されるときは、1か0であ ることが保証されている。(これはisalopha()のようなライブラリ関 数にはあてはまらない。) 9.3: pがポインタだとして if(p) は正しい条件か。 A: 正しい。質問5.3を参照のこと。 10章 Cプリプロセッサ 10.2: ちょっとしたマクロを書いて、CのコードをPascal風に見えるように してみた。感想は。 A: 最低。 10.3: 2つの値を交換する汎用のマクロは。 A: この質問に確かな回答はない。一番の万能の解決方法はマクロを使う ことを考えないことだ。 10.4: 複数の文(multi-statement)からなるマクロを書くにはどうすれば いいか。 A: #define Func() do {stmt1; stmt2; ... } while(0) /* (最後に;は なし) */ 10.6: .hって何? 何を入れればいいの。 A: ヘッダファイル(「.hファイル」とも呼ばれる)は共通の宣言や、マク ロ、構造体、typedefの定義を入れるものである。ただし変数や関数 の定義は入れない。 10.7: ヘッダが、別のヘッダを#includeすることは許容されているのか。 A: これは書き方に関する問題であり、この質問に関する議論は盛り上が る。 10.8: ヘッダ(#include)ファイルを探すのに、どこを探しにいくのか。 A: 動作の細かいところは処理系依存である。検索場所についての論議は 長篇を参照して欲しい。 10.9: ファイルの最初の宣言で奇妙な構文エラーが出た。よさそうに見える のに。 A: #includeしたファイルの最後の宣言の終わりにセミコロンを付けるの を忘れたのだろう。 10.11: 欠けているヘッダファイルはどこから手に入れればいいのか。 A: コンパイラを買ったところに相談する。質問18.16と長篇FAQも参照 のこと。 10.12: 文字列を比較するプリプロセッサの#if式はどうすれば作れるか。 A: 直接には不可能である。わかりやすいラベルを整数定数に#defineし て、それらのラベルを使った条件を実装するしかない。 10.13: #ifの中でsizeof()は使えるか。 A: 使えない。 10.14: #ifdefを#defineの行に使って、あるものを2つのまったく異なった 風に定義することができるか。 A: できない。 10.15: typedefで使える#ifdefのようなものがないか(typedef済みかどうか で処理を切り替えることはできるか)。 A: 残念ながら存在しない。 10.16: プリプロセッサの#ifを使って、マシンがビッグエンディアンかリト ルエンディアンかを知ることができるか。 A: たぶんできない。 10.18: コードを条件コンパイルの一組だけ残して、cppを通すことなく、か つ#includeや#defineは展開しないで前処理する方法があるか。 A: unifdefとかrmifdefとかscppとかいう名前のプログラムを探すこと。 10.19: あらかじめ#defineされたマクロの一覧を得るにはどうすればいいか。 A: まずはコンパイラの資料を読む。それでは不十分なら、印字可能な文 字列をコンパイラやプリプロセッサの実行可能プログラムから拾いだ す。 10.20: 古いコードがあって、その中では識別子をマクロ #define Paste(a, b) a/**/b を使って作り出していた。でももう動 かなくなってしまった。 A: ANSIの、トークンを連結する演算子##を使う。 10.22: メッセージ「警告:文字列リテラルの内部のマクロ置換」は何を指し ているのか。 A: 質問11.18を参照。 10.23-4:文字列リテラルの中で#演算子を使ってマクロの引数を展開したい がうまくいかない。 A: 質問11.17と11.18を参照。 10.25: コンパイル時に凝った前処理をしたいが、どうやればいいかわからな い。 A: 目的に応じた専用の前処理を行うツールを作ることを考えたほうがい い。 10.26: 可変個の引数を取るマクロをどうやって書けばいいか。 A: 以下は人気のある技である。printfの引数リストを囲む括弧は、マク ロの定義のものではなくマクロの呼び出しのものであることに注意。 #define DEBUG(args) (printf("DEBUG: "), printf args) if(n != 0) DEBUG(("n is %d\n", n)); 11章 ANSI/ISO規格C 11.1: 「ANSI C 規格」とは何を意味するのか。 A: 1983年に、アメリカ国内標準規格協会(ANSI)はC言語の標準化を目指 し委員会X3J11を発足させた。委員会の作業は、アメリカ国内標準規 格X3.159として1989年12月14日に承認され、国際標準ISO/IEC 9899:1990として採択された。後に追補が加わっている。 11.2: どうやれば規格書を入手することができるか。 A: ニューヨークのANSIまたはコロラド州EnglewoodのGlobal Engineering Documentsまたは各国の規格を取り仕切る団体(日本では 日本規格協会)またはジュネーブのISOから入手可能である。本の中に 規格を再録しているものもある。 11.2a: 規格の改訂の進み具合に関する情報はどこから得られるか。 A: 長篇FAQを足掛かりにすること。 11.3: 私が使っているANSIコンパイラは、floatと宣言した引数でプロトタ イプと不一致だと文句を付ける。 A: 新しい書き方であるプロトタイプ宣言の extern int func(float); と、 古い書き方の定義の int func(x) float x; を混ぜて使っているからで ある。"狭い"型の新旧どちらの立場に立つかで解釈が違ってくる。こ の問題は狭い型を使うのをやめるか、新しい書き方(プロトタイプ)だ けにするか古い書き方だけにするかすることで解決する。 11.4: 古い書き方と新しい書き方の関数の構文を混ぜて使ってもいいのか。 A: 今のところ、大半の引数で、まったく合法である。 11.5: なぜ宣言 extern f(struct x *p); で警告メッセージが出るのか。 A: プロトタイプ内で初めて宣言された構造体は(名前が出てくるだけで も)、同じソースファイル内で宣言されている他の構造体と互換性を 持つことができない。 11.8: なぜconstの値を、初期化子(initializer)や配列の大きさに使え ないのか。 A: constと修飾されたオブジェクトは、定数式という用語の持つさまざ まな意味を考えると定数式とは違う。 11.9: char const *pとchar * const pの違いは。 A: char const *pは文字定数へのポインタで、char * const pは文字へ のポインタ定数である。 11.10: 関数プロトタイプにconst char **を引数として取ると宣言した関数 に、char **を渡すことができないのはなぜか。 A: 修飾付きの(constやvolatileが付いてる)ポインタ型に少しの不一致 を許している規則は、再帰的に適用されるわけではない。 11.12: main()をvoidとして宣言して「mainの戻り値がない」という目障りな メッセージを消すことができるか。 A: できない。 11.13: main関数の3番目の引数envpは。 A: これは(よく見かけるけれど)標準でない拡張である。 11.14: void main()と宣言してうまくいかないわけがないと思う。なぜなら main()から戻る代わりに、exit()を呼んでいるから。 A: main()から戻ってくるかどうかは関係ないし、そのステータスを見る かどうかも関係ない。問題はmain()を正しく呼び出すことすらできな いかもしれないことにある。 11.15: 僕がいつも使っている本には、いつもvoid main()と書いてある。 A: その本は間違っている。 11.16: exit(status)の値は、main()からの戻り値statusと本当に等しいのか。 A: 等しいときもあるし違うこともある。(詳細については長篇版FAQを参照 して欲しい。) 11.17: ANSIの"文字列を作り出す"プリプロセッサの演算子#を使って、マク ロの名前ではなくその値を文字列にするのはどうやればいいか。 A: #defineを2段階で使って、マクロを文字列にするだけでなく展開もさ れるようにしなければならない。 11.18: 「警告:マクロ展開が文字列リテラル内で発生」というメッセージは 何を意味しているのか。 A: ANSI規格成立より前のコンパイラ/プリプロセッサには、マクロの引 数は文字列リテラルや文字定数の中でも展開するものがあった。 11.19: #ifdefで囲んで無効にしたコードの内側で、奇妙な構文エラーが発生 した。 A: ANSI Cでは#if、#ifdef、#ifndefによってコンパイルから"無効にされた" テキストも「コンパイル前処理のトークンとして有効なものである」 としている。よって引用符で囲まれた部分に改行があってはならないし、 終端のないコメントや引用符(例:短縮した単語のアポストロフィ) があってはならない。 11.20: #pragmaとは何物か。 A: #pragmaは、機能の拡張のための、うまく定義された唯一の"非常口" である。 11.21: #pragma onceは何を意味しているのか。 A: いくつかのプリプロセッサで、ヘッダファイルの「べき等」化 (2度以上読み込まれても問題が発生しないようにするのを)を容易 にするための拡張として用意されている。 11.22: char a[3] = "abc";は正しいのか。 A: ANSI Cでは文法的に正しい。 11.24: なぜvoid *ポインタを相手に算術演算をすることができないのか。 A: コンパイラにはポインタが指す先のオブジェクトの大きさがわからな いからだ。 11.25: memcpy()とmemmove()の違いは。 A: memmove()は、コピー元とコピー先に重なりがあったときもうまく扱 うことを保証している。 11.26: malloc(0)は、どういう動作をすべきなのか。 A: 動作は処理系依存(質問11.33参照)である。 11.27: なぜANSI規格は、外部識別子の7文字目以降に意味があることや大文 字小文字の違いを保証しないのか。 A: 問題は古いリンカにある。古いリンカを(規格にこうしろと書いたところで )アップグレードさせることはできない。 11.29: 私の使っているコンパイラは、考えられる限りの一番単純なテストプ ログラムに対しても、ありとあらゆる文法エラーではねつける。 A: 君が使っているのはきっとANSI規格が決まる前に作られたコンパイラ なんだろう。 11.30: ANSIコンパイラを使っているのに、ANSI/ISO規格のライブラリルーチ ンで未定義となるものがあるのはなぜか。 A: ANSI互換のヘッダファイルやランタイムライブラリがないのだろう。 11.31: 誰か古い書き方のCプログラムをANCI Cに変換するプログラムや、ま た自動的に関数プロトタイプを生成するツールを持っていないか。 A: 詳細については長篇FAQを参照して欲しい。 11.32: なぜANSI準拠が売り物のCコンパイラ「轟天」が、このコードをはね つけるのか。 A: はねられたコードが、規格外の拡張機能を使っていない自信があるか。 11.33: 処理系定義の(implementation-defined)動作、未規定の (unspecified)動作、未定義の(undefined)動作の違いは? A: 移植性のあるコードを書くのなら上の3つの違いを無視するべし。 移植性のないコードを書かざるをえなくなったら長篇を参照し て欲しい。 11.34: ANSI規格には多くの論点が未定義のまま放置してあることを考えると 心配で夜も眠れない。 A: 規格の大部分はそれまでに存在した習慣を単に明文化したにすぎない。 11.35: ANSI準拠のコンパイラでは未定義とされているコードを試してみたら、 結果は私の予想した通りだった。 A: 未定義の動作に出くわしたらコンパイラは好きなように振る舞う。そ の中には君が期待した結果も含まれる。今回は偶然一致したわけだ。 12章 標準入出力ライブラリ 12.1: なぜ以下のコードはうまく動かないのか。 char c; while((c = getchar()) != EOF) ... A: getchar()の戻り値を格納する変数はintでなければならない。 12.2: なぜ以下のコードは最後の行を2回コピーするのか。 while(!feof(infp)) { fgets(buf, MAXLINE, infp); fputs(buf, outfp); } A: Cでは、入力ルーチンが実際に読もうとしてファイルの終わり (End-Of-File)にたどり着いた後に初めてEOFが立つからだ。 12.4: 自作のプログラムのプロンプトと中間出力が、画面上にあらわれない ことがある。 A: 出力が見えて欲しいところでは、必ず明示的にfflush(stdout)を使っ て出力をはきだすこと。 12.5: RETURNキーが押されるのを待つことなく、一度に一文字ずつ読むこと ができるか。 A: 質問19.1を参照のこと。 12.6: どうすれば、printfのフォーマット文字列を使って'%'を出力できる のか。 A: "%%" 12.9: scanf()が%lfを使うのに、どうしてprintf()は%fを使うのか。 A: Cの"省略時の引数の格上げ"により、データ型floatの変数はdoubleに 格上げされるからだ。 12.10: printf()でフィールドの幅を可変に実装するにはどうすればいいのか。 A: printf("%*d", width, n)を使う。 12.11: 千単位でコンマで切って数を表示するのはどうすればいいか。 A: 標準のルーチンというのは存在しない(ただしは要チェック)。 12.12: なぜコードscanf("%d", i);がうまく動かないのか。 A: scanf()には、引数として必ずポインタを渡さなければならない。 12.13: なぜ以下のコードは動かないのか。 double d; scanf("%f", &d); A: printf()と違ってscanf()ではdoubleには%lfを、floatには%fを使う。 12.15: scanf()の書式文字列で可変の幅を指定するのはどうやればいいか。 A: できない。 12.17: scanf()で"%d\n"を使ってキーボードから数を読み取ると、もう1行余 計に打ち込むまで、ハングするようだ。 A: "%d\n"ではなく"%d"を使う。 12.18: scanfの%dを使って数字を読んで、文字列をgets()で読もうとしてい る。けれどコンパイラはgets()の呼び出しを飛ばしているようだ。 A: scanf()とgets()の相性は悪い。 12.19: scanf()に失敗したらユーザーにプロンプトを返している。けれど、 たまに無限ループに入ってしまうようだ。 A: scanf()は腐った入力で"ジャム"ってしまう。腐った入力を捨てないか らだ。 12.20: どうして誰もがscanf()を使わないほうがいいというのか。代わりに 何を使えばいいのか。 A: scanf()には数多くの問題がある。たいていの場合は1行丸々読み込ん でから解釈するほうがずっと簡単である。 12.21: sprintf()の呼び出しで、書く先のバッファがどれくらいの大きさが必 要かどうすればわかるか。sprintf()の呼び出しで書く先のバッファを、 どうすればあふれさせずにすむか。 A: この2つの鋭い質問には立派な解答は(まだ)ない。 12.23: なぜgets()を使うなと誰もが言うのか。 A: 入力バッファがあふれてしまうことを防ぐことができないからだ。 12.24: なぜprintf()を呼ぶと変数errnoにENOTTYが設定されるのか。 A: 心配しなくていい。プログラムでerrnoの中身を調べることに意味があるのは、 エラーが返されたときだけだ。 12.25: fgetpos/fsetposとftell/fseekの違いは。 A: fgetpos()とfsetpos()は、特別なtypedefであるfpos_tを使ってオフ セットを表現する。fpos_tが適切に選ばれていれば(longのオフセッ トに縛られた) ftell()やfseek()を使うよりも大きなファイルを扱う ことができる可能性がある。 12.26: fflush(stdin)で標準入力のストリームに残っている未読の文字を捨 てることができる。 A: できない。 12.30: fopenのモード"r+"を使ってファイルを開いて、そのファイルの実体 上で上書きして更新しようとしている。でもうまくいかない。 A: 書いたり読んだりする前に必ずfseek()を必ず呼ぶこと。 12.33: プログラムの中からstdinやstdoutの先をファイルにリダイレクトす るにはどうすればいいか。 A: freopen()を使う。 12.34: freopen()を使った後で、元のstdout(あるいはstdin)に戻すことがで きるか。 A: よい方法は存在しない。freopen()なんて使わないようにする。 12.38: バイナリデータのファイルを、ちゃんと読むにはどうすればいいか。 A: fopen()を呼ぶ際に、必ず"rb"モードを指定する。 13章 ライブラリ関数 13.1: どうすれば数を文字列に変換することができるか。 A: なにも考えずにsprintf()を使え。 13.2: なぜstrncpy()はコピー先の文字列に、終端文字の'\0'を付けないこ とがあるのか。 A: ちょっと興味深い歴史的な理由による。 13.5: どうしてtoupper()の中には大文字を与えると変な動きをするものが あるのか。 A: 古い版のtoupper()やtolower()はこの点で正しく動かない場合があっ た。 13.6: 文字列を空白文字列によって区切られたフィールドに分けるのはどう やればいいか。 A: strtok()を使ってみよう。 13.7: 正規表現とかワイルドカードを使った比較をするコードが必要となっ た。 A: 正規表現のライブラリは数多くある。詳細については長篇を参照して 欲しい。 13.8: 文字列の配列をqsort()でソートするのに、strcmp()を比較用の関数 として使用しているが、うまくいかない。 A: 比較のための補助の関数を書く。この比較に使う関数の引数は汎用の ポインタを2つ取る。これらの引数をchar **に変換し、それを間接参 照しよう。これでchar *が得られるので、比較に使える。 13.9: qsort()を使って構造体の配列をソートしようとしている。けれどコ ンパイラは、私の関数がqsort()の引数としては間違ったデータ型だ と文句を付ける。 A: 比較に使う関数は"汎用のポインタ" (const void *)を引数として取 ると宣言されていなければならない。これを構造体へのポインタに変 換する。 13.10: リンク付きリストをどうやってソートすればいいか。 A: 挿入ソート(insertion sort)やマージソート(merge sort)のようなア ルゴリズムはリンク付きリストといっしょに使うのに向いている。こ のほかに、リストを作っている間ずっとリストの要素の順が乱れない ようにしておく手もある。 13.11: メモリに収まりきらない量のデータをどうやってソートすればいいか。 A: 「外部ソート(external sort)」を使えばいい。詳細については長篇 FAQを参照して欲しい。 13.12: Cのプログラムで時刻を得るのはどうすればいいか。 A: 関数time()とctime()かlocaltime()、またはこれらのすべてを使う。 13.13: 構造体tmや文字列からtime_tへの変換はどうやって行えばいいか。 A: ANSI Cは、構造体tmをtime_tに変換するライブラリ関数mktime()を用 意している。 時間を表す文字列を解析(parse)する方は標準のルーチ ンはない。 13.14: 日付の引き算や足し算はどうやればいいか。 A: ANSI/ISO規格のC言語は、mktime()とdifftime()という関数を、上の 要求の一部に答えるために用意している。 13.15: 乱数発生器が欲しい。 A: 標準のCライブラリにrand()というのが存在する。 13.16: ある範囲の整数からなる乱数はどうやったら生成することができるか。 A: 1つのやり方は、 (int)((double)rand() / ((double)RAND_MAX + 1) * N) 13.17: 私のプログラムを走らせるたび、いつも関数rand()から同じ乱数列が 返ってくる。 A: srand()を使って、擬似乱数発生器に本当にランダムな初期値を与え ればいい。 13.18: 真偽値からなる乱数が欲しいのでrand() % 2を使ったところ、結果は 0と1が交互に現れるだけだった。 A: 上位のビットを使おう。質問13.16を参照のこと。 13.20: 正規分布つまりガウス分布の乱数を生成するのはどうすればいいか。 A: ヒントは長篇版FAQを参照して欲しい。 13.24: 古いプログラムを移植しようとしている。なぜ「未定義の外部シンボ ル」というエラーが出るのか。 A: ヒントは長篇版FAQを参照して欲しい。 13.24: 古いプログラムを移植しようとしている。どうして"未定義の外部シ ンボル(undefined external)"というエラーがライブラリ関数に対し て出るのか。 A: よく使われていたけど標準にならなかった関数の中には、名前を変え られたり別の関数に取って代わられたものがある。詳細については長 篇FAQを参照して欲しい。 13.25: 「ライブラリのルーチンが未定義」というエラーが出たままである。 正しいヘッダファイルをすべて#includeしたのに。 A: プログラムをリンクするときに正しいライブラリが見つかるように、 パスを明記しなければならないのかもしれない。 13.26: それでも「ライブラリのルーチンが未定義」というエラーが出たまま である。今度はリンクするときにライブラリを明示的に指定したのに。 A: ライブラリの検索順序には意味がある。普通はライブラリを最後に探 すように指定すべきだ。 13.28: リンカが_endが未定義だと文句をつけてくるのはどういうときか。 A: このメッセージが出るのは、他にも未定義のものがあるときだけであ る。 14章 浮動小数点 14.1: 浮動小数点の変数を、例えば3.1に設定すると、どうしてprintf()は 3.0999999と出力するのか。 A: たいていのコンピュータは浮動小数点数に、整数にと同じように、2 進数を用いている。多くの小数が(0.1も含めて)、2進数では正確に表 現できない。 14.2: どうしてsqrt(144.)の結果がとんでもない数になるのか。 A: まずを#includeしたことを確認して、次にdoubleを返す関数 を正しく宣言したことを確認する。 14.3: 「_sin未定義」というコンパイルエラーが止まらない。 A: まずは数学関数のライブラリが本当にリンクされていることを確認す ること。 14.4: 浮動小数点の計算の結果が変で、しかもマシンによって違った答えが返っ てくる。 A: まずは上の質問14.2を参照すること。問題がそんなに単純でないとき は、長篇FAQに簡潔な説明が出ている。まともなプログラムの本なら よりよい方法が載っている。 14.5: 2つの浮動小数点の値が"十分近い"ことを判定するよい方法は。 A: 2つの浮動小数点の値を比較する最上の方法は精度のしきい値を使う ことである。精度のしきい値は、比較しあう数の大きさに比例させる。 14.6: 数を丸めるにはどうすればいいか。 A: 正の数なら(int)(x + 0.5)を使う。 14.7: Cのべき乗演算子はどれなんだろう。 A: pow()を使おう。 14.8: 私が使ってるマシンのからあらかじめ定義されているはずの 定数M_PIが漏れているようだ。 A: この定数は標準ではない。 14.9: IEEEのNanとか、その他の特別な値かどうかのテストはどうやればいい か。 A: 移植性の高い方法はまだない。 しかし長篇FAQにヒントが載っている。 14.11: Cで複素数を実装するよい方法は。 A: 簡単なのは、単純な構造体とその構造体を操作する算術関数をいくつ か用意することである。 14.12: 数値演算ライブラリのソースコードを探している。 A: Ajay Shahがフリーで手に入る算術ソフトウエアの目録を管理してい る。これはrtfm.mit.eduのディレクトリcomp.lang.cにアーカイブさ れている(質問20.40も参照のこと)。 14.13: Turbo Cでプログラムを実行すると「浮動小数点フォーマットがリン クされていない」といってクラッシュするので困っている。 A: 浮動小数点数関連機能を強制的にロードさせるように、浮動小数ライ ブラリを使うルーチンを余分に呼ばなければならないかもしれない。 15章 可変個数実引数リスト 15.1: printf()を呼ぶ前に必ずを必ず#includeするようにと言わ れた。なぜ? A: printf()の正しいプロトタイプがスコープに入るようにするためにで ある。 15.2: どうして%fをprintf()のfloatの変数にもdoubleの変数にも使うこと ができるのか。この2つは別のデータ型ではないのか。 A: 可変個数実引数リストでは、charとshort intはintに、floatは doubleにそれぞれ格上げされる。 15.3: どうしてANSIの関数プロトタイプはprintf()の引数の型の不一致から 守ってくれないのか。 A: プロトタイプは可変個数の引数の数やそのデータ型についての情報を 教えてくれないので、チェックのしようがない。 15.4: 可変個数の引数を取る関数をどうやって書けばいいか。 A: にある機能を使う。 15.5: printf()のように書式文字列と可変個数の引数をもらって、引数を printf()に渡して、仕事のほとんどprintf()にやらせるような関数を、 どうやって書けばいいのか。 A: vprintf()かvfprintf()かvsprintf()を使う。 15.6: scanf()に類似の関数を書いて、scanf()を呼んでほとんどの仕事をさ せたい。どうすればいいか。 A: 残念ながら、vscanfの類は標準ではない。自分で書くしかない。 15.7: ANSI規格決定前に開発されたコンパイラを使っているが、 がない。どうすればいいか。 A: 古いヘッダというのがあって、これがほとんど同じ機能 を提供する。 15.8: 関数が、実際にいくつ引数を渡されたか知る方法はあるか。 A: 可変個数引数を取る関数は、引数自身から引数の数を判断できるよう に作らなければならない。 15.9: 今使っているコンパイラは可変個数の引数しかない関数を宣言させて くれない。固定の引数なしでは駄目なようだ。 A: 標準Cは少なくとも1つ固定の引数を必要とする。 15.10: どうして va_arg(argp, float) ではうまくいかないのか。 A: 可変個数の実引数リストの可変個数の部分には、古い「省略時の引数 格上げ」が適応される。必ず va_arg(artp, double) と書かなければ ならない。 15.11: va_arg()マクロを使ったが、関数へのポインタのデータ型を持つ引数 を取り出すことが私の技量ではできない。 A: typedefを使おう。 15.12: 可変個数の引数を持ち、その引数を別の関数(その関数も可変個数の引 数を取る)に渡す関数をどうやって記述すればいいか。 A: 一般的にはできない。 15.13: 実行時に、引数リストをつくって関数を呼び出すことができるか。 A: できない。 16章 奇妙な問題 16.2a: わけがわからない文法エラーが出た。プログラムの大部分がコンパイ ルされないままのように見える。 A: コメントを閉じ忘れてないかとか、プリプロセッサ指令 (#if/#ifdef/#ifndef/#else/#endif)の対応が取れていないとかをチェッ クしよう。 16.2b: どうして手続き呼び出し(procedure)がうまくいかないのか。 A: たとえ引数がなくても、関数呼び出しには括弧でくくった引数リスト が必要である。 16.3: このプログラム、走る前に死んでしまう。 A: 巨大なローカルの配列がないか調べよう。 (質問11.12, 16.4, 16.5, 18.4.も参照のこと。) 16.4: このプログラムは正しく動いているように見えるけれど、最後のとこ ろでクラッシュする。 A: 長篇FAQにヒントが載っている。 16.5: このプログラム、あるマシンではうまく走るのに、別のマシンだと変 な結果を返す。 A: 長篇版FAQは、いくつか可能性のある場合について説明している。 16.6: なぜ以下のコードはクラッシュするのか。 char *p = "hello, world!"; p[0] = 'H'; A: 文字列定数は、(実際上)配列の初期化子として使われるときを除いて は、変更可能ではない。 16.8: 「セグメンテーション違反(Segmentation violation)」とは何を意味 するのか。 A: これは一般に、アクセスすべきでない記憶領域にプログラムがアクセ スしようとしたことを意味している。きっとスタックが壊れたかポイ ンタの使い方が適切でなかったからに違いない。 17章 スタイル 17.1: Cのコードの配置で最良のものはどんなものか。 A: 絶対の"最上の書き方"は存在しない。ただし、長篇FAQにはヒントが 載っている。 17.3: if(!strcmp(s1, s2)) はよい書き方? A: 別に。 17.4: どうして if(x == 0) と書く代わりに、if(0 == x) と書く人がいる のか。 A: これはよく if(x = 0) と書いてしまうことを防ぐためのコツである。 (訳注:本のほうには、これはとくによい書き方というわけではないと 書いてある。実際これでは両辺が変数の場合の if(a = b) という誤 りを捉えることはできない。こんな小手先の技を覚える暇があれば、 lintの使い方を覚えること。) 17.5: printf()を呼び出しているところすべてに、いちいち(void)のキャス トを付けているコードを見かけた。なぜこんなことをするのか。 A: 戻り値を読まずに捨てると警告が出るので、この警告が出ないように するため。 17.8: 「ハンガリー記法(Hungarian Notation)」とは。使う価値があるか。 A: ハンガリー記法は名前の付けかたの取り決めで、これは変数の型を変 数の名前の中に略号として埋めこむものである。 17.9: Indian Hillスタイルガイドやそのほかのコーディング規約をどこから 手に入れることができるのか。 A: 長篇版FAQを読んで欲しい。 17.10: gotoは悪で絶対に使ってはならないという人がいる。ちょっとこれは 行き過ぎではないか。 A: そうだね。良いプログラムの書き方をガチガチの規則で決めようとす るのは完璧からほど遠い方法だ。 18章 道具と資源 18.1: Cの開発ツール(クロスリファレンスを生成するツールとか、整形ツー ルなど)を探している。 A: 長篇版FAQにプログラムの名前がいくつか挙っている。 18.2: malloc()の厄介な問題はどうやって追跡すればいいか。 A: 長篇版FAQにツールの一覧が出ている。 18.3: フリーまたは安く手に入るCコンパイラにはどのようなものがあるか。 A: 長篇版FAQにちょっとした目録が出ている。 18.4: プログラムを打ち込んだところだけれど、このプログラムは奇妙な動 きをする。どこか変なところがあるか。 A: まずはlintを走らせてみよう。 18.5: mallocを呼ぶたびにlintが出す「警告:ポインタの整合に問題の可 能性あり(warning:possible pointer alignment problem)」という メッセージを消す方法はあるか。 A: grep -vでも使って勝手に無視するほうが手軽だろう。 18.7: ANSI互換のlintはどこで手に入るか。 A: 長篇版FAQに製品プログラムが2つ紹介されている。 18.8: ANSIの関数プロトタイプが用意されたことでlintは時代遅れになった のか。 A: まだまだ。よくできたコンパイラならlintの診断の大半をやってくれ るだろうが、lintの行う全部の診断を行うコンパイラは少ない。 18.9: Cの独習書やその他の知的資源をインターネットで入手することはで きるか。 A: いくつか存在する。 18.10: C言語を学ぶのによい本は? A: Cに関する本は多すぎて、ここで名前を全部挙げることは不可能であ る。取っ掛かりとして長篇版FAQを見て欲しい。 18.13: 標準のCライブラリのソースはどこにあるのか。 A: 長篇版FAQに、入手できる可能性のあるものがいくつか挙っている。 18.14: 式を構文解析して、式の演算結果を得るコードが欲しい。 A: 入手可能なパッケージの話が長篇版FAQに出ている。 18.15: C言語の文法をBNFやYACCで書いたものは、どこで手に入るか。 A: ANSI規格そのものか、長篇版FAQを参照にして欲しい。 18.15a: Cコンパイラの妥当性を検査する一連のテストをだれか持っていない か。 A: 長篇版FAQに入手先が出ている。 18.15c: 役に立ちそうなコードの切れ端や例を集めたものはどこから入手可能 か。 A: 長篇版FAQに入手先が載っている。 18.16: どこから、これらのパブリックドメインのプログラムを手に入れるこ とができるのか。 A: comp.sources.unixやcomp.sources.miscに定期的に投稿されている記 事を参照のこと。長篇版FAQにも入手方法が出ている。 19章 システム依存 19.1: Returnキーが押されることを待つことなしにキーボードから1文字読 むにはどうすればいいのか。 A: 嘆かわしいことに、Cには上のようなことを行う標準的な方法も移植 性の高い方法もない。 19.2: 読み込むことができる文字が残っているかどうか(できるなら後いく つ残っているか)知ることができるか。文字が入ってこないときに処 理が止まってしまわないような読み込みはどうやればいいか。 A: これも、まったくオペレーティングシステムに依存した問題である。 19.3: 処理の何%まで終了したかを表示して、それをその場で更新させるに はどうすればいいか。また仕事の進んでいることを"バトンを回す"こ とで知らせるのはどうやればいいのか。 A: 文字'\r'は復帰(carriage return)、文字'\b'はバックスペースを表す。 (訳注:;\r'を出力するとカーソルはその行の先頭に戻る。 '\b'を出力すると1カラム戻る。) 19.4: どうすれば画面に表示されている文字を一掃することができるか。 どうすれば反転文字で出力することができるか。 どうすればカーソルを動かすことができるか。 A: 中途半端だけれど移植性の高い解はcursesライブラリを使うことだろ う。 19.5: 矢印キーを読むにはどうしたらいいか。ファンクションキーは。 A: こういうのはキーボードやオペレーティングシステムや使えるライブ ラリに依存する。 19.6: どうすればマウスの出力を読むことができるか。 A: どんなシステムを使ってるの? 19.7: シリアル(COM)ポートを使った入出力はどうやればいいか。 A: それはシステム次第である。 19.8: 出力先をどうすればプリンターに切り替えることができるか。 A: 長篇版FAQにヒントが載っている。 19.9: エスケープシーケンスを使って、端末やその他のデバイスを制御する にはどうすればいいのか。 A: エスケープシーケンスを直接送り込む。ASCIIでは、ESCのコードは' \033'である。 19.10: グラフィックするには? A: 移植性の高い方法はない。 19.11: ファイルが存在するかどうかはどうやって調べればいいか。 A: access()やstat()という関数を使ってみよう。これでうまくいかなけ れば、動くことが保証されていて移植性が高いのは、ファイルを実際 に開いてみるしかない。 19.12: どうすれば読む前にファイルの大きさを知ることができるか。 A: stat()やfseek()/ftell()でおおよその大きさを知ることができる (ただし長篇版FAQに注意事項が載っている)。 19.12a: ファイルがいつ変更されたかはどうすればわかるか。 A: stat()を使ってみよう。 19.13: まったく消し去ることや上書きすることなくファイルをその場で短く することはできるか。 A: いくつか方法はあるが、移植性の高い方法はない。 19.14: ファイルの真ん中に1行(あるいは1レコード)挿入する、あるいは削除 するのはどうやればいいか。 A: ファイルを書き直すのでなければ、たぶん不可能だろう。 19.15: オープンしたファイルのファイル識別子からファイル名を復活するこ とができるか。 A: この問題は、たいてい解決不能である。オープンしたファイルの名前 を自分で覚えておくのが一番よい。 19.16: どうやればファイルを削除することができるか。 A: 標準Cのライブラリ関数にremove()がある。 19.17: どうして、 fopen("c:\newdir\file.dat", "r") だとうまくいかないのか。 A: バックスラッシュを2回続けて書くとたぶんうまくいく。 19.18: 同時に開けるファイルの数をどうすれば増やせるのか。 A: システムに付いてきた資料を読もう。 19.20: ディレクトリの内容を読むにはどうすればいいか。 A: opendir()やreaddir()が使えるかどうか調べよう。 19.22: どれだけメモリが使えるかはどうやればわかるか。 A: オペレーティングシステムが、そういう情報を返すルーチンを用意し ているかもしれない。 19.23: どうやれば64Kより大きな配列や構造体を割り付けることができるの か。 A: オペレーティングシステムによっては、そんなこと許してくれない。 19.24: 「DGROUPデータの割り付けが64Kを越えた(DGROUP data allocation exceeds 64K)」というエラーメッセージは何を表しているのか。 A: スタティックなデータが多過ぎるということである。 19.25: あるアドレスに位置するメモリにどうやってアクセスすればいいか。 A: ポインタの値を絶対アドレスに設定する。 19.27: Cプログラムの中から別のプログラムを起動したい。どうすればいい か。 A: system()を使う。 19.30: 別のプログラムを実行して、その出力を捕まえるのはどうすればいい のか。 A: Unixや他のいくつかのシステムはpopen()ルーチンを用意している。 19.31: どうすればプログラムが起動されたときの絶対パスを知ることができ るか。 A: argv[0]に絶対パスやパスの一部が入っているかもしれない。シェル のようなコマンド行を解釈するソフトがコマンドを探す筋道を真似る 方法もある。 19.32: 実行可能プログラムと同じディレクトリにあるプログラムの設定ファ イルがどこにあるかを自動的に探したい。どうやればいいか。 A: これは難しい。上の質問19.31も参照のこと。 19.33: どうすればプロセスが、起動した側の環境変数を変更することができ るか。 A: できるとしてもその方法は、システムに依存する。 19.36: どうすればオブジェクトファイルを読み込んで、その中にあるルーチ ンに飛び込むことができるか。 A: ダイナミックリンカやダイナミックローダが必要となる。 19.37: 1秒より細かい遅れや、ユーザの反応の1秒より細かい計測は、どう実 装すればいいか。 A: 残念ながら移植性の高い方法は存在しない。 19.38: Control-Cのようなキーボード割り込みを捕まえたり無視するのはど うすればいいか。 A: signal()を使う。 19.39: 浮動小数点の例外を扱う気のきいた方法は。 A: matherr()やsignal(SIGFPE)を見てみよう。 19.40: えーっと、どうやってソケットを使えばいいのか。ネットワークは。 クライアント/サーバーのアプリケーションの書き方は。 A: これらの質問は、C言語よりは使用するネットワーク環境との関係 のほうが強い。 19.40b: BIOSコールをどうやって使えばいいのか。どうすれば常駐ソフトが書 けるのか A: こういうのは特定のシステムに特有な話である。 19.41: そんなこといっても、そんな標準でないシステム依存の関数を使うわ けにはいかない。私のプログラムはANSI規格に適合してなければいけ ない。 A: それは無理な相談というものだ。現実問題に対処するプログラムであ れば1つや2つはANSIが定義していないサービスが必要となる。 20章 その他 20.1: 関数から複数の値を返すことはできるか。 A: 値を格納する領域をいくつか起動する側で用意して、それらの領域を 指すポインタを引数として渡し、関数にポインタの先に値を埋めても らう。あるいは、関数の戻り値を構造体にして、その構造体に希望の 値を設定してもらう。 20.3: コマンド行の引数にどうやってアクセスすればいいか。 A: main()の引数argvを使おう。 20.5: データの表現が違うマシンでも読めるようにデータファイルに書き込 むにはどうすればいいか。 A: 最も移植性が高い解はテキストファイルを使うことである。 20.6: 関数名が文字列で用意されているときに、どうすればこの関数を呼ぶ ことができるか。 A: 一番単純な方法は、関数の名前と関数へのポインタの対応表を用意す ることである。 20.8: ビットの集合や配列はどうやって実装すればいいのか。 A: charやintの配列を使う。マクロを使って、配列のしかるべき要素に ある望みのビットにアクセスできるようにする。 20.9: マシンのバイトの並び順(byte order)がビッグエンディアンかリトル エンディアンかはどうやって調べればいいか。 A: 通常は、ポインタか共用体を使った技を使う。 20.10: 整数をどうやれば2進数や16進数に変換できるか。 A: 整数は内部的には2進数で保存されている。入出力中は、何進数を使 うかを選ぶことができる。 20.11: 2進数の定数(0b101010のように)を使うことはできるか。2進数用の printf()の書式は存在するか。 A: 2進数の定数は存在しないし、2進数用の書式も存在しない。 20.12: 値の中で立っているビットの数を数えるもっとも効率のよい方法を教 えて欲しい。 A: この手の"ビットをいじる問題"は参照用テーブル(lookup table)を使 うことで、高速かつ能率よく処理することができる。 20.13: どうすれば、このコードをもっと効率的にすることができるか。 A: よいアルゴリズムを選んで、それを注意深く実装する。 20.14: ポインタは本当に配列よりも高速なのか。関数呼び出しをするとどれ だけ遅くなるのか。 A: これらの質問や多くの類似の質問への正確な解答は、使用しているプ ロセッサやコンパイラに依存する。 20.17: 文字列の内容によってswitch風に処理を切り替える方法はあるか。 A: 直接には不可能である。 20.18: caseのラベルに定数以外(範囲指定とか、こちらの好きな式など)を持っ てくることはできるか。 A: できない。 20.19: returnの後ろに来る式をくくる括弧は本当に省略可能か。 A: 省略可能だ。 20.20: なぜC言語のコメントは入れ子にすることができないのか。引用符で かこまれた文字列の中にコメントがあるのは文法上許されるのか。 A: Cのコメントが入れ子にできないのは、PL/Iのコメントが入れ子にで きないからである。/*や*/は二重引用句でくくられた文字列のなかで は特別な意味を持たない。 20.24: どうしてCには入れ子の関数が用意されていないのか。 A: Cが単純さを失わないように意図的に省略された。 20.25: どうすればCからFORTRAN(あるいはC++、BASIC、Pascal、Ada、LISP) の関数を起動することができるか。 A: 解答は、使用中のマシンやコンパイラが関数を起動する方法に依存す る。 20.26: PascalあるいはFORTRANをCに変換するプログラムを知らないか。 A: 自由に配布可能なプログラムがいくつか存在する。例えばptocや p2cやf2cというのがある。詳細については長篇版FAQを参照して欲しい。 20.27: C++のコンパイラをCのコードのコンパイルに使うことはできるか。 A: 必ずしもできるとはかぎらない。うるさいことをいうとC++はCの 上位互換ではない。 20.28: 2つの文字列が近い、けれど必ずしもぴったりでなくてもいい、こと を調べるルーチンが欲しい。 A: 長篇版FAQにヒントが載っている。 20.29: ハッシュ法とは。 A: ハッシュ法とは文字列(あるいはその他のデータ構造)を、検索が容易 になるように整数に写像する処理のことをいう。 20.31: 日付から曜日を求める方法は。 A: mktime()かZellerの公式を使う。長篇版FAQにはその他の方法のコー ドも載っている。 20.32: 紀元2000年はうるう年か。 A: うるう年だ。 20.34: 出力として、自分自身のソースコードを産み出すコードはどう書けば いいのか。 A: これでどうだ。 char*s="char*s=%c%s%c;main(){printf(s,34,s,34);}"; main(){printf(s,34,s,34);} 20.35: "ダフのデバイス(Duff's Device)"とは。 A: とんでもなく曲がりくねって展開されたバイトコピーの関数である。 詳細については長篇版FAQを参照して欲しい。 20.36: 次の国際難解Cコードコンテスト:International Obfuscated C Code Contest(IOCCC)はいつ開かれるのか。どうすれば昔の授賞作を手に入 れることができるのか。 A: 長篇版FAQを読む。あるいはjudges@toad.comにメールを出す。 20.37: K&R1で述べられていたentryというキーワードは。 A: このキーワードは、関数が複数の異なる名前のエントリーポイントを 持つことを許すことを考えて予約されていた。このキーワードは撤回 された。 20.38: そもそも、「C」という名前の由来は。 A: Cはプログラミング言語Bから派生したものである。BはBCPLから着想 を得ている。BCPLはCPLを単純にしたものである。 20.39: 「char」というのはどう発音するのか。 A: 英単語の「char(チャー)」、「care(ケア)」、「car(カー)」のよう に発音する(どれを使ってもいい)。 20.40: このリストをもう一部欲しいがどうしたらいいか。過去の版はどうか。 A: 最新版はftp.eskimo.comのディレクトリu/s/scs/C-faq/から入手する ことができる。ネットからも引っ張ってくることができる。通常、ニュー スグループcomp.lang.cに月の始めに投稿される。Expires:行が付い ているので一ヶ月丸々消えないで残っている。このFAQはニュースグ ループcomp.answersやnews.answersに投稿されている。いくつものサ イトがこのFAQも含めて、news.answersへの投稿やその他のFAQを保管 している。サイトrtfm.mit.edu(ディレクトリ pub/usenet/news.answers/C-faq/とpub/usenet/comp.lang.c/)やサイ トftp.uu.net (ディレクトリusenet/news.answers/C-faq/) は、その ようなサイトのうちの2つである。archieサーバー(質問18.16参照)は そのようなサイトを見つける助けになる。 このFAQの別の形のものが、 http://www.eskimo.com/~scs/C-faq/top.html から入手可能である。 このFAQの拡張した版はAddison-Wesleyより『C Programming FAQs: Frequently Asked Questions』(ISBN 0-201-84519-9)として1995年11 月に出版されている。 (訳注: 日本語版はトッパンより『CプログラミングFAQ Cプログラミングの よく尋ねられる質問』として出版されている。) Steve Summit scs@eskimo.com この記事の著作権は1990年から1996年に渡ってSteve Summitに帰属する。書籍 『C Programming FAQs: Frequently Asked Questions』の内容を、著者と出版 社の許可の元、社会への貢献のために使用している。このFAQは書籍版を補足 することを意図している。内容は各国の著作権法によって守られる。個人使用 にかぎっては自由に参照してよい。ただし許可なく再出版することは許さない。 (訳注:日本語版の著作権は北野 欽一に帰属する。) This article is Copyright 1990-1996 by Steve Summit. Content from the book _C Programming FAQs: Frequently Asked Questions_ is made available here by permission of the author and the publisher as a service to the community. It is intended to complement the use of the published text and is protected by international copyright laws. The content is made available here and may be accessed freely for personal use but may not be republished without permission.