Newsgroups: fj.archives.answers,fj.lang.c From: kitano@crd.yokogawa.co.jp (Kinichi - Kinchan - Kitano) Subject: comp.lang.c Answers to Frequently Asked Questions (FAQ List) in Japanese[2/4] Sender: news@leia.pa.yokogawa.co.jp (Leia news server) Message-ID: Supersedes: Date: Mon, 14 Jul 1997 17:29:59 GMT Reply-To: kitano@crd.yokogawa.co.jp Organization: Yokogawa Electric Corporation, Tokyo, Japan. Followup-To: fj.lang.c Lines: 1283 Archive-name: c-faq-j/part2 Last-modified: 14 July. 1997 ========================= C FAQ 日本語訳[2/4] ========================= 6章 配列とポインタ 6.1: あるソースファイルでchar a[6]と定義して、別のファイルでextern char *aと宣言した、なぜこれはうまくいかないのか。 A: extern char *aという宣言が、実際の定義と食い違うからである。 「タイプTへのポインタ」は「タイプTの配列」とは異なる。extern char a[]を使え。 References: ANSI Sec. 3.5.4.2; ISO Sec. 6.5.4.2; CT&P Sec. 3.3 pp. 33-4, Sec. 4.5 pp. 64-5. 6.2: でもchar a[]はchar *aと同じと聞いたことがあるが。 A: 全然別のものである。(君が聞いたのは、関数の仮引数の話だ。質問 2.4参照)。配列はポインタと違う。配列の宣言「char a[6]」は6文字 分の領域を確保して、それを「a」という名前で識別することを要求 する。すなわち「a」という名前の場所があって、そこには6文字を収 めることができる。一方、ポインタの宣言「char *p」はポインタを 収める場所を要求する。ポインタはpという名前で識別され、ほとん どどんなものでも指すことができる。どんなchar、あるいは連続した charの列を指すこともできるし、どこも指さなくても構わない(質問 5.1と1.30参照)。 例によって、百聞は一見に如かずである。文 char a[] = "hello"; char *p = "world"; は以下のように表現できるデータ構造を初期化する。 +---+---+---+---+---+---+ a: | h | e | l | l | o |\0 | +---+---+---+---+---+---+ +-----+ +---+---+---+---+---+---+ p: | *======> | w | o | r | l | d |\0 | +-----+ +---+---+---+---+---+---+ x[3]を参照したときに産み出されるコードが、xがポインタか配列か で違うのだと理解することは大事なことである。上記の宣言を与えら れたとして、コンパイラはa[3]という式を見たところで、「a」のと ころから始めて、そこから3つ進んで、そこにある文字を取り出す、 というコードをはきだす。p[3]という式を見ると、「p」に進み、そ こに存在するポインタの値を取り出し、ポインタの値に3を加え、最 後にポインタが指す場所から文字を取り出す、というコードをはきだ す。言い換えればa[j]はaという名前のオブジェクト(の先頭)から3つ 目の場所で、p[3]はpが指すオブジェクトから3つ目の場所である。上 の例ではa[3]、p[3]はたまたま同じ文字'l'を指すがコンパイラはそ こにたどり着くのに別の道をたどるのである。(本質的な違いは、aの ような配列の値とpのようなポインタの値は式のどこであらわれよう と別の方法で計算されるということである。次の質問でさらに説明す るように、添字付けされてようとなくても。) References: K&R2 Sec. 5.5 p. 104; CT&P Sec. 4.5 pp. 64-5. 6.3: Cで"ポインタと配列は同等"というのは何を意味しているのか。 A: Cにおけるポインタに関する混乱の多くは、上の文の誤解から来て る可能性がある。配列とポインタが"同等"といっても、この2つが まったく同じとか交換可能であるということは意味していない。 "同等"というのは、この問題を解く手掛かりになる以下の定義を指し ている。 式中に現われる型「Tの配列」という左辺値(3つの 例外を除いて)、配列の最初の要素を指すポインタ に意味が格下げになる。結果としてできるポインタ の型は「Tへのポインタ」となる。 (3つの例外とは、配列がsizeofの引数となるとき、アドレス演算 子&の引数となるとき、char型の配列を文字列リテラルで初期値す るとき、である)。 この定義により、演算子[]を配列に使っても、ポインタに使っても たいして違いはない。 a[i]と書いたとき、上の規則により配列の参 照「a」はポインタへと成り下がる。これでポインタ変数に対し てp[i]と書くのと同じことになってしまった(ただし質問6.2で説明し たように、メモリのアクセスのしかたは異なるかもしれない)。配列 のアドレスをポインタに代入することになったら、 p = a; p[3]とa[3]は同じ要素を指す。 質問6.8も参照のこと。 References: K&R1 Sec. 5.3 pp. 93-6; K&R2 Sec. 5.3 p. 99; ANSI Sec. 3.2.2.1, Sec. 3.3.2.1, Sec. 3.3.6; ISO Sec. 6.2.2.1, Sec. 6.3.2.1, Sec. 6.3.6; H&S Sec. 5.4.1 p. 124. 6.4: それではなぜ関数の仮引数では配列とポインタの宣言が交換できる のか。 A: そのほうが便利だからだと考えられているからである。 配列はすぐにポインタに成り下がるので、配列が実際に関数に渡る ことはない。ポインタ引数を配列のように見せて宣言することを許 すのは、配列が渡されたように見せるためである。プログラマは仮 引数が昔から配列のように扱われるとか、配列が(厳密に言えば配列 のアドレスだが)昔から渡されるということを強調したいのかもしれ ない。コンパイラに都合がいいように、配列のように"見える"仮引数 宣言は、以下に例を挙げるように f(a) char a[]; { ... } すべてコンパイラによってポインタが渡されたかのように扱われる。 なぜなら配列が渡されたときに、関数が実際に受け取るのはポインタ であるからである。 f(a) char *a; { ... } この変換は、関数の仮引数の仮引数宣言でのみ有効である。その他の 場所では起こらない。この変換が気に食わないなら使わなければよい。 関数の呼び出しや関数内での使い方に宣言を似せることによる小さ な利点よりも、それが引き起こす混乱のほうが大きいと考える人は多 い。 質問6.21も参照のこと。 References: K&R1 Sec. 5.3 p. 95, Sec. A10.1 p. 205; K&R2 Sec. 5.3 p. 100, Sec. A8.6.3 p. 218, Sec. A10.1 p. 226; ANSI Sec. 3.5.4.3, Sec. 3.7.1, Sec. 3.9.6; ISO Sec. 6.5.4.3, Sec. 6.7.1, Sec. 6.9.6; H&S Sec. 9.3 p. 271; CT&P Sec. 3.3 pp. 33-4. 6.7: どうして配列は代入できないのに左辺値(lvalue)なのか。 A: ANSI C規格は「変更可能な左辺値(modifiable lvalue)」を定義して いる。その中に配列は入っていない。 References: ANSI Sec. 3.2.2.1 p. 37. 6.8: 実際のところ、配列とポインタの違いは。 A: 配列は自動的に領域を割り付ける。ただし別の場所に移したり大きさ を変えることはできない。ポインタは、どこかに割り付けられた 領域(たぶんmalloc()を使って割り付けた領域)を指すアドレスを明示 的に代入しなければならない。そのかわり好きなように値を変えるこ とができる(すなわち別のものを指すことができる)。またメモリブロッ クを指す以外にも色々と使い道がある。 俗にいう配列とポインタが同等(質問6.3参照)により、配列とポイン タが同じに見える場合が多い。特にmalloc()により割り付けた領域を 指すポインタが、しばしば本物の配列のように(実際[]を使って参照 することができる)扱われる。質問6.14と6.16を参照のこと(ただし sizeofには要注意)。 質問1.32と20.14を参照。 6.9: 配列とは定数ポインタにすぎないと説明してくれた人がいた。 A: それはちょっとものごとを単純に考えすぎである。配列の名前は、名 前には代入できないという点で"定数"であるが、質問6.2の議論と図 を見れば配列がポインタでないことははっきりするだろう。質問6.3 と6.8を参照。 6.11: 5["abcdef"]という式を含んだジョークのコードを見たことがある。 どうしてこの式がC言語で文法上正しいことになるのか。 A: 配列の添字演算子[]の二つのオペランドは交換可能で、Y[X]と書いて もX[Y]と書いても同じ意味になるというのをご存知ない? この奇妙 な事実は、配列の添字付けのポインタの定義からきている。すなわち、 一方がポインタを表す式で、残りが整数である限り、どんなaとeをもっ てきても、a[e]は*((a)+(e))と同じものであるという定義である。こ のとんでもない交換可能性は、よくC言語について扱う文章の中で、 誇らしく思うかのように記述されているが国際難解Cプログラムコン テスト以外では役に立たない(質問20.36参照)。 References: Rationale Sec. 3.3.2.1; H&S Sec. 5.4.1 p. 124, Sec. 7.4.1 pp. 186-7. 6.12: 配列を参照することはポインタに成り下がることを考えれば、arr という配列があったとしてarrと&arrの違いは。 A: データ型。 ANSI/ISO Cの元では&は「Tの配列へのポインタ」を産み出す。これ は配列全体を指す。(ANCI Cが誕生する前は、arrの前に&を付けるこ とは、たいてい警告を招き、たいてい無視された。) すべてのCコン パイラで配列への(キャストのない)参照はポインタを産み出す。こ のポインタはTへのポインタで配列の最初の要素を指す((質問 6.3, 6.13, 6.18も参照のこと) References: ANSI Sec. 3.2.2.1, Sec. 3.3.3.2; ISO Sec. 6.2.2.1, Sec. 6.3.3.2; Rationale Sec. 3.3.3.2; H&S Sec. 7.5.6 p. 198. 6.13: 配列へのポインタをどうやって宣言するのか。 A: たいていは、そんなポインタを宣言したいのではない。なにげなく 配列へのポインタというときは、たいてい配列の最初の要素へのポ インタのことをいっているのである。 配列へのポインタではなく、配列の要素へのポインタを使うことを考 えること。型Tの配列は型Tへのポインタに成り下がる(質問6.3を参照)。 これは都合がよい。なぜなら結果としてできるポインタを使って添字 つきで参照したり、整数を加えることで配列の各要素にアクセスした りできる。これに対して、本当の配列へのポインタは、添字つきで参 照したり整数を加えると、配列全体を飛び越してしまう。これではせ いぜい配列の配列を扱うときにしか役立たない(上の質問6.18を参照)。 本当に配列そのものへのポインタが必要な場合は「int (*ap)[N];」 のような表現を使う。ここでNは配列のサイズを表す(質問1.21も参照)。 配列の大きさがわからない場合、Nを省略することができる。しかし 結果として得られる「大きさが未知の配列へのポインタ」は役に立た ない。 上の質問6.12も参照のこと。 References: ANSI Sec. 3.2.2.1; ISO Sec. 6.2.2.1. 6.14: どうすれば配列の大きさをコンパイル時に設定することができるか。 固定の大きさの配列を使わなくて済ますにはどうすればいいか。 A: 配列とポインタが同等(質問6.3を参照のこと)であることによって mallocしたメモリを指すポインタを使って配列をかなり効果的に 真似ることができる。 #include int *dynarray: dynarray = malloc(10 * sizeof(int)); を実行した後では(かつmalloc()の呼び出しが成功したら)、dynarray で、普通の静的に割り付けた配列(int a[10])のように扱って dynarray[i] (0から9までのiに対して)を参照することができる。質 問1.31a, 6.16, 7.7も参照のこと。 6.15: 引数として渡された配列の大きさに合ったローカルな配列をどうやっ て宣言することができるか。 A: Cでは不可能だ。配列の大きさはコンパイル時に定数でなければなら ない。(gccはパラメータ付き配列を拡張機能として提供している。) malloc()を使って、関数から返る前にfree()を必ず呼ばなければなら ない。質問6.14, 6.16, 6.19, 7.22を参照のこと。 質問7.32を参照 する必要があるかもしれない。 References: ANSI Sec. 3.4, Sec. 3.5.4.2; ISO Sec. 6.4, Sec. 6.5.4.2. 6.16: 多次元の配列を動的に割り付けるのはどうしたらよいか A: たいていポインタの配列を割り付けて、それぞれのポインタを動 的に割り付けた"列"に初期化するのが一番の解決策である。以下に2 次元配列の例を挙げる。 #include int **array1 = (int **)malloc(nrows * sizeof(int *)); for(i = 0; i < nrows; i++) array1[i] = (int *)malloc(ncolumns * sizeof(int)); (もちろん"本物の"コードではmallocの戻り値のチェックが必要であ る。) 配列の中身をメモリ上連続にすることもできる。ただしこうすると、 後で一つ一つの列を再割り付けするのが面倒になる。以下のような 明示的な、ちょっとしたポインタ計算が必要となる。 int **array2 = (int **)malloc(nrows * sizeof(int *)); array2[0] = (int *)malloc(nrows * ncolumns * sizeof(int)); for(i = 1; i < nrows; i++) array2[i] = array2[0] + i * ncolumns; どちらを選択しても、動的に割り付けた配列の各要素は普通の配列の ように添え字でアクセスできる。 (0 <= i <= NROWS かつ 0 <= j <= NCOLUMNSで) arrayx[i][j] というふうに。 もし上のような二重間接アクセス方法がなんらかの理由で受け入れら れないなら、一つの動的配置の1次元配列で2次元配列を真似ることが できる。 int *array3 = (int *)malloc(nrows * ncolumns * sizeof(int)); しかしこうすると添字の計算を自分で行わなければならない、すなわ ちi,j番目の要素へのアクセスは array3[i * ncolumns + j]としなけ ればならない(マクロを使えば明示的な計算を隠すことができる。し かしマクロを使えば括弧やコンマを使う必要があるので多次元配列 へのアクセスのようには見えなくなる。同じくマクロは少なくとも一 つの次元の大きさを知る必要がある。質問6.19も参照のこと)。 最後に配列へのポインタを使う方法を紹介する。 int (*array4)[NCOLUMNS] = (int (*)[NCOLUMNS])malloc(nrows * sizeof(*array4)); けれど、この構文は読む人に恐怖感を与えるし、コンパイル時に一つ の次元を除くすべての次元が確定していなければならない。 どの方法をとるにしても、必要がなくなったら配列を解放することを 憶えておかなければならない(それは、何段かの手続きを踏まなけれ ばならないかもしれない)。また配列を動的に割り付けて他の関数に 渡す際に、渡す先の関数が、静的に割り付けた普通の配列を引数とし て受けとる場合は特に注意が必要である(質問6.20参照のこと。また 質問6.18も参照のこと)。 上のどの技法も3次元以上の配列に拡張することが可能である。 6.17: ほらこのトリック。下のように書けば int realarray[10]; int *array = &realarray[-1]; arrayは1始まりの配列のように使える。 A: このテクニックは魅力的だが(NUMERICAL RECIPES IN Cの古い版でも 使われている)、厳密にいえばC言語の規格に従っていない。ポインタ 演算は、一度に割り振られた領域と、仮想的な"終端"を越えた1つめ の要素にだけ定義されていて、それ以外では未定義である。このこと は、たとえポインタを参照に使っていないとしてもあてはまる。上の コードはオフセットを引いた時に、とんでもないアドレスを作り出し て(たぶんアドレスが"ぐるっと回って"メモリセグメントの先頭を越 えて他のセグメントと重なるからだろう)うまくいかなくなる可能性 がある。 References: K&R2 Sec. 5.3 p. 100, Sec. 5.4 pp. 102-3, Sec. A7.7 pp. 205-6; ANSI Sec. 3.3.6; ISO Sec. 6.3.6; Rationale Sec. 3.2.2.3. 6.18: 私が使っているコンパイラは、ポインタへのポインタを使うべき ところで2次元配列を使うと不満をいう。 A: 配列がポインタに成り下がるという規則は(質問6.3参照)、再帰的に は成り立たない。配列の配列(例えばC言語における2次元配列)は、配 列へのポインタに成り下がるのであって、ポインタへのポインタに成 り下がるわけではない。配列へのポインタは、混乱を招くから、注意 して扱わなければならない。質問6.13も参照のこと。(混乱は行儀の 悪いコンパイラ、が多次元配列を多段のポインタとして受け付けるこ とで増長されている。そういうコンパイラとしてはpccの古い幾つか のバージョンと、そういうpccから派生したlintが含まれる)。 もし2次元配列を以下の関数に渡すと int array[NROWS][NCOLUMNS]; f(array); 関数の宣言は以下のどちらかでないといけない。 f(int a[][NCOLUMNS]) { ... } あるいは f(int (*ap)[NCOLUMNS]) /* apは配列へのポインタ */ { ... } 最初の宣言では、コンパイラが仮引数を「配列の配列」から「配列へ のポインタ」へ通常通り暗黙の書き換えを行う(質問6.3と6.4を参照)。 2つ目の定義では、ポインタの定義であるとはっきり書いてある。呼 ばれる側の関数は配列分の領域を取るわけではないので、配列全体の 大きさを知る必要はない。列の数「NROWS」は省略することができる。 配列の"形"は大事であるから行の数「NCOLUMS」を(3次元以上の配列 の場合は最初の次元をのぞくすべての次元の大きさを含めて)指定し なければならない。 もしポインタへのポインタを受け付けると関数が宣言していると きには、直接2次元配列を渡すことはやっても意味がないだろう。 質問6.12と6.15も参照のこと。 References: K&R1 Sec. 5.10 p. 110; K&R2 Sec. 5.9 p. 113; H&S Sec. 5.4.3 p. 126. 6.19: コンパイル時に"幅"が未定の2次元配列を引数とする関数はどうやっ て書けばよいか。 A: これは結構難しい。一つは[0][0]要素へのポインタを、2つの次元の それぞれの大きさと一緒に渡して配列の添字付けを"手で"真似る方法 がある。 f2(aryp, nrows, ncolumns) int *aryp; int nrows, ncolumns; { ... ary[i][j] は実際にはaryp[i * ncolumns + j] ... } この関数は質問6.18の配列を使って以下のように呼ぶこともできる。 f2(&array[0][0], NROWS, NCOLUMNS); しかしながら このような方法によって"手で"多次元の配列の添字付 けをするプログラムはANSI C規格に厳密には従っていないことに注意 すること。x >= NCOLUMNSの場合に(&array[0][0])[x]にアクセスした ときの動作は定義されていない。 gccを使えば、関数の引数を使ってローカルの配列がある大きさを持 つと宣言することができる。しかしこれは標準ではない拡張機能であ る。 様々な大きさの多次元配列を引数に取る関数を使いたいなら、質問 6.16のようにすべての配列を動的に真似るやりかたがある。 質問6.18, 6.20, 6.15も参照のこと。 6.20 関数の引数として配列を渡すときに、静的に割り付けた配列も動的に 割り付けた配列も受け付けるようにするにはどうしたらよいか。 A: 唯一の完全な解というのは存在しない。以下の宣言があって、 int array[NROWS][NCOLUMNS]; int **array1; /* ragged */ int **array2; /* contiguous */ int *array3; /* "flattened" */ int (*array4)[NCOLUMNS]; 質問6.16と同じようにポインタを初期化するとする。また以下のよう に宣言した関数が存在するとする。 f1(int a[][NCOLUMNS], int nrows, int ncolumns); f2(int *aryp, int nrows, int ncolumns); f3(int **pp, int nrows, int ncolumns); 関数f1()は通常の2次元の配列を引数とし、f2()は"平らにした"2次元 の配列を引数とし、f3()はポインタを指すポインタを引数として配列 を真似るとする(質問6.18と6.19も参照のこと)。以下の呼び出しはこ ちらの期待通りの動きをする。 f1(array, NROWS, NCOLUMNS); f1(array4, nrows, NCOLUMNS); f2(&array[0][0], NROWS, NCOLUMNS); f2(*array, NROWS, NCOLUMNS); f2(*array2, nrows, ncolumns); f2(array3, nrows, ncolumns); f2(*array4, nrows, NCOLUMNS); f3(array1, nrows, ncolumns); f3(array2, nrows, ncolumns); 以下の2つの例もたいていのマシンで多分うまくいく。ただし怪しい キャストが含まれているし、動的に割り付けたncolumnsが、静的に割 り当てたNCOLUMNSと一致するときしかうまく動かない。 f1((int (*)[NCOLUMNS])(*array2), nrows, ncolumns); f1((int (*)[NCOLUMNS])array3, nrows, ncolumns); ここでも&array[0][0]をf2に渡すのは(*arrayを渡すのも)厳密には規 格に準拠していないことに注意すること。質問6.19を参照。 もしなぜ上記の関数呼び出しがすべてうまくいくかということと、な ぜ上記のように記述されたかがわかっていて、洩れている組み合わせ がなぜうまくいかないかもわかっているのなら、C言語の配列とポイ ンタについての知識は結構いい線いっていると思っていいだろう。 さまざまな大きさの多次元の配列の、上に書いたようなことを気にす る代わりの扱いかたとしては、すべての配列を質問6.16にあるように 動的にする方法がある。静的な多次元の配列がなければ、つまりすべ ての配列が質問6.16のarray1やarray2のように割り付けられているな ら、すべての関数はf3()のように書くことができる。 6.21: なぜサブルーチンの引数として渡された配列の大きさをsizeof()でき ちんと計算できないのか。 A: コンパイラは配列仮引数がポインタと宣言されたように振る舞い (質問6.4参照)、sizeof()はポインタの大きさを返す。 References: H&S Sec. 7.5.2 p. 195. 7章 メモリの割り付け 7.1: 以下のプログラムはなぜ動かないのか。 char *answer; printf("なにか入力してください:\n"); gets(answer); printf("あなたは \"%s\" と入力しました\n", answer); A: ポインタ変数answerは応答を貯える場所として関数gets()に渡される が、その時点ではanswerは有効な領域を指していない。すなわちポイ ンタanswerは、どこを指しているのかわからない(ローカルな変数 は初期化されることはないし、たいていゴミが入っている。「answer」 がヌルポインタとして始まることさえ保証されていない。質問1.30 と5.1を参照のこと)。 上の質問のプログラムを修正する一番やさしい方法は、ポインタの代 わりにローカルの配列を使って、コンパイラに領域の割当てをまかせ ることである。 #include #include char answer[100], *p; printf("なにか入力してください。:\n"); fgets(answer, sizeof answer, stdin); if((p = strchr(answer, '\n')) != NULL) *p = '\0'; printf("あなたは \"%s\"と入力しました\n", answer); この例はgets()の代わりにfgets()を使って、配列に続く部分が上書 きされないような工夫もしている。(質問12.23参照。残念ながらこの 例ではfgets()はgets()とは違って、後ろの改行を自動的には削除し ない)。malloc()を使って返答用のバッファを割り付けることもでき る。 7.2: strcat()がうまく動かない。以下のプログラムを走らせたら、 char *s1 = "Hello, "; char *s2 = "world!"; char *s3 = strcat(s1, s2); 変な答えが返ってきた。 A: 上の質問7.1と同じように、ここでも一番の問題は連結した結果を貯 える領域がうまく割り付けられていないことである。C言語には、自 動的に管理される文字列型はない。ソースコードで明示的に表された オブジェクトに領域を割り付けるだけである(ここでいう"文字列"に ついてはcharの配列と"でくくられた文字列を含む)。プログラマは、 文字列の連結のような実行時の操作の結果に対して十分な領域を、配 列を定義したり、malloc()を起動することで明示的に割り付けなけれ ばならない。 strcat()は、領域の割り付けを行わない。2番目の文字列は最初の文 字列に、その場で連結される。一つの解決方法は、最初の文字列を十 分な大きさを持つ配列として宣言することである。 char s1[20] = "Hello, "; strcatは第一引数を返すので(この場合はs1)、s3は余計である。 質問のコードの元のstrcat()の呼び出しは、実際には二つの難点があ る。s1によって指される文字列リテラルが、連結されてできるどんな テキストも保存できるほど大きくないかもしれないだけではなく、そ もそも書き込み不可かもしれない。質問1.32参照。 References: CT&P Sec. 3.2 p. 32. 7.3: けれどstrcat()のmanページによると、strcat()は引数として2つの charへのポインタを取ることなっている。領域の割り付けが必要で あるとどうやって知ることができるのか。 A: 一般にポインタを使うときは、いつも領域の割り付けのことを考えて おかなければならない。少なくともコンパイラが代わりにやってくれ ることを確認しておかなければならない。ライブラリのドキュメント に記憶領域の割り付けについてはっきり記述していなければ、割り当 ては普通は使う人の責任である。 Unixのmanの先頭やANSI C規格の要約の章は、誤解を招くかもしれな い。そこに載っているコードの一部は、使い方というよりは、関数の 実装で使われる関数定義に近い。とくに(構造体や文字列への)ポイン タを扱う関数の多くはなんかしらのオブジェクト(構造体か配列への … 質問6.3や6.4を参照)へのアドレスを引数に起動される。ほかによく ある例はtime() (質問13.12参照)やstat()である。 7.5: 文字列を返すはずの関数がある。けれど呼んだ側の関数に返ってくる と、返ってきた文字列にはゴミが入っている。 A: 関数が返す文字列を格納する領域が正しく割り付けられていることを 確かめること。返されたポインタは静的に割り付けられたバッファか、 呼んだ側の関数から渡されたバッファか、malloc()により得られたメ モリを指すべきで、呼ばれた関数のローカルな(自動変数の)配列を指 していてはいけない。つまり以下のようなことは絶対にやってはいけ ない。 char *itoa(int n) { char retbuf[20]; /* 間違い */ sprintf(retbuf, "%d", n); return retbuf; /* 間違い */ } 修正する方法の1つは、バッファを以下のように宣言することである (これも、関数が再帰的に起動される場合や、戻り値が同時に複数の 箇所で必要な場合にうまく行かないことを考えると不十分である)。 static char retbuf[20]; 質問12.21と20.1も参照のこと。 References: ANSI Sec. 3.1.2.4; ISO Sec. 6.1.2.4. 7.6: どうしてmalloc()を呼ぶと「警告: 整数をポインタに代入の際には キャストが必要」というのが出るのか。 A: を#includeしたか、してないとするとmalloc()が正しく宣 言されるようにしたか。質問1.25も参照のこと。 References: H&S Sec. 4.7 p. 101. 7.7: mallocが返した値を割り付けたデータ型のポインタに注意深くキャス トしているコードをたまに見るのはなぜか。 A: ANSI/ISO規格のC言語がvoid *という汎用のポインタ型を導入する までは、互換性のないポインタ型の間で代入をするときに警告を黙 らせるの(もしかしたら変換もさせるのに) にこうしたキャストが必要となることがよくあった。 ANSI/ISO規格のCでは、こうしたキャストはもう必要ない。それどこ ろか今では思いとどまった方がよいと主張することも可能である。こ うしたキャストはmalloc()がたまたま正しく宣言されたかったときに、 大事な警告を隠してしまう可能性があるからである。上の質問7.6も 参照のこと。 References: H&S Sec. 16.1 pp. 386-7. 7.8: 以下のようなコードを見た。 char *p = malloc(strlen(s) + 1); strcpy(p, s); malloc((strlen(s) + 1) * sizeof(char))では? A: sizeof(char)を掛ける必要がある場合は絶対にない。なぜなら sizeof(char)は定義によりぴったり1であるから。 (一方、 sizeof(char)をかけても何も害はない。こう書くことで式にsize_tが 現れて理解しやすくなるかもしれない。)質問8.9も参照のこと。 References: ANSI Sec. 3.3.3.4; ISO Sec. 6.3.3.4; H&S Sec. 7.5.2 p. 195. 7.14: オペレーティングシステムによってはmallocしたメモリを実際に確 保するのをプログラムがそのメモリを使おうとするまで先延ばしす ると聞いたことがある。これは文法上許されるのか。 A: これは難しい。規格は、システムはこういう風に振る舞ってもよいと 書いていないけれど、こういう風に振る舞ってはいけないとはっきり 書いてあるわけでもない。 References: ANSI Sec. 4.10.3; ISO Sec. 7.10.3. 7.16: 数値演算をするのに、大きな配列を割り付けようと考えている。下の コードを書いたところ、 double *array = malloc(300 * 300 * sizeof(double)); malloc()はヌルを返すわけではないけれど、プログラムの動きが変だ。 メモリを上書きしたり、こっちが望んだだけmalloc()が割り付けてな かったりとかするようだ。 A: 300 x 300は90,000で、これはsizeof(double)を掛ける前から、16ビッ トの整数には納まらないことに注意(質問1.1参照)。こんなに大きな メモリを確保する必要があるときは、注意する必要がある。使ってい るマシンのsize_t(malloc()が認めたデータ型)が32ビットであれば、 300 * (300 * sizeof(double))と書くことでやっていけるかもしれな い(質問3.14参照)。これで駄目ならデータの構造をもっと小さな単位 に分解するか、32ビットのマシンを使うか、標準ではないメモリ割り 付けルーチンを使うかしなければならない。質問19.23も参照のこと。 7.17: 私のPCには8MBもメモリが載っている。どうして640Kかそこらし かmalloc()できなさそうなのか。 A: PC互換機のセグメント付きのアーキテクチャーでは、640Kより多くの メモリを使うことは非常に難しい。質問19.23も参照のこと。 7.19: プログラムがコケる。malloc()の内部のどこかのようだ。でもどこが 悪いか分からない。 A: mallocした領域の内部のデータ構造は残念ながら非常に簡単に壊れて しまう。しかも引き起こされる障害は追跡しにくいものとなることが ある。いちばんよくある障害の元は、mallocした領域に割り付けた大 きさよりも多く書き込んでしまうことである。特によくある例は、大 きさstrlen(s)+1ではなく、大きさstrlen(s)だけmalloc()で割り付け ることである。他にはfreeした領域を指すポインタを使うことや、2 回freeしたり、malloc()で割り付けた先を指していないポインタを free()を使って開放しようとしたり、ヌルポインタを使って realloc()を呼び出すことも障害を引き起こす(質問7.30を参照)。 質問7.26, 16.8, 18.2も参照のこと。 7.20: 動的に割り付けた記憶領域は解放した後には使えないね。 A: 使えない。malloc()の昔の解説には、解放された領域は「壊されずに 残っている」と記述してあるものもあった。このうかつな保証は一般 的になることはなく、C規格では、このようなことを保証することは 要求されていない。 解放した領域の中身を意識して使うプログラマは少ない。けれど偶然 使ってしまうことはよくある。一方向リンク付きリストを解放する以 下の(正しい)コードを考えてみよう。 struct list *listp, *nextp; for(listp = base; listp != NULL; listp = nextp) { nextp = listp->next; free((void *)listp); } 一時変数のnextpを使うことなくlistp = listp->nextを使ったとすれ ばどうなったか考えてみること。 References: K&R2 Sec. 7.8.5 p. 167; ANSI Sec. 4.10.3; ISO Sec. 7.10.3; Rationale Sec. 4.10.3.2; H&S Sec. 16.2 p. 387; CT&P Sec. 7.10 p. 95. 7.21: どうしてポインタがfree()を呼んだ後でヌルポインタにならない のか。freeした後のポインタの値を使う(代入、比較する)ことは、 どれくらい危険なのか。 A: free()を呼ぶと、free()に渡したポインタの指す先のメモリは解 放されるが、呼び出した側のポインタの値は変わらない。それはC の値渡しとは、呼ばれた側の関数が自分の引数を決して変えることは ないということだからである。(質問4.8も参照) 解放されたポインタの値は厳密にいえば無効で、それをどう使って も、たとえ間接参照以外のことに使っても理屈の上ではトラブルの元 である。ただ、これは実装の質の話だけれど、無効なポインタの害 のない使い方にわざわざ例外を発生させる実装はたぶん存在しない。 References: ANSI Sec. 4.10.3; ISO Sec. 7.10.3; Rationale Sec. 3.2.2.3. 7.22: ローカルなポインタ用にメモリをmalloc()で割り付ける。わざわざ free()を呼ばなければならないか。 A: もちろん。ポインタとポインタが指す先は別物であることを忘れては いけない。ローカル変数は関数から戻るときに解放される。ただしポ インタ変数に関しては、ポインタが解放されるのであって、ポインタ が指す先が解放されるわけではない。malloc()によって割り付けられ たメモリは明示的に解放するまで必ず残る。一般に、すべての malloc()の呼び出しに、対応するfree()がなければならない。 7.23: 動的に割り付けたオブジェクトへのポインタを含む構造体を割り付け ている。構造体を解放する前に、構造体が含むポインタの先のオ ブジェクトを全部解放しなければならないのか。 A: そのとおり。一般にmalloc()が返してきたポインタを(解放するとし たら)それぞれ一度だけfree()に渡すしくみを用意しなければいけな い。おおざっぱな指針としてはプログラム内のmallocの各呼出しに対 してそのmallocの呼出しで割り付けたメモリを解放するfreeがどれか プログラマは答えられなければならない。 質問7.24も参照のこと。 7.24: プログラムが終了する前に、割り付けたメモリを解放しなければなら ないか。 A: その必要はない。まともなオペレーティングシステムならきっとプロ グラムが終了した時点ですべてのメモリを取り返すだろう。にもかか わらず、個人向けコンピュータ(PC)の中にはメモリを取り戻すことが 確実にはできないものもあるようである。ANSI/ISO C規格から結論付 けられることは、解放してくれるかどうかは「実装の品質がどれくら い高いかによる」ということだけである。 References: ANSI Sec. 4.10.3.2; ISO Sec. 7.10.3.2. 7.25: 大量の記憶領域をmallocしてfreeしてまわるプログラムがある。けれ ど、メモリの使用状況をみると(psで見える値では)領域が戻ってきた ようには見えない。 A: たいていのmalloc/freeの実装は、解放されたメモリをオペレーティ ングシステムに(オペレーティングシステムがあったとして)返さない。 後からmalloc()が呼び出されたときのために取っておく。 7.26: free()は、何バイト解放するかをどうやって知るのか。 A: malloc/freeの実装は、メモリのブロックを割り付けて、そのブロッ クの先頭アドレスを返す。その時に、ブロックのサイズを記憶する。 よって解放するときにfreeに思い出させる必要はない。 7.27: だったら割り付けた領域の大きさをmallocパッケージに聞くことがで きるのか。 A: 移植性の高い方法では不可能である。 7.30: realloc()の最初の引数にヌルポインタを使うことは許されている のか。どうしてそんなことをするのか。 A: ANSI Cはこの使用方法を許している(これに関係するrealloc(...,0) も許している。これが領域を解放する)。けれども昔のコンパイラに は対応していないものもあるので、この方法は移植性が高いとはいえ ない。第1引数をヌルポインタにすることで、割り付ける領域をだん だん増やしていくアルゴリズムを実現するときに、起動する部分を書 くことが容易になる。 References: ANSI Sec. 4.10.3.4; ISO Sec. 7.10.3.4; H&S Sec. 16.3 p. 388. 7.31: calloc()とmalloc()の違いは。calloc()の0を埋めるという機能を、 ポインタや浮動小数点数に使っても問題ないか。free()はcalloc() が割り付けた領域にも働くか、それともcfree()を使わなくてはなら ないか。 A: calloc(m, n)は実質的に以下のコードと同じ動きをする。 p = malloc(m * n); memset(p, 0, m * n); 0を埋めるというのは全ビット0にするということで、ヌルポインタや浮 動小数点の0で埋めることを保証していない(5章 を参照のこと)。calloc()が割り付けた領域の解放にfree()を使うこ とにも正しく使える。 References: ANSI Sec. 4.10.3 to 4.10.3.2; ISO Sec. 7.10.3 to 7.10.3.2; H&S Sec. 16.1 p. 386, Sec. 16.2 p. 386; PCS Sec. 11 pp. 141,142. 7.32: alloca()とは何者で、なぜ使わないほうがよいと人は言うのか。 A: alloca()は領域を割り付け、alloca()を起動した関数を抜けた時点で その領域は自動的に解放される。すなわちalloca()によって割り付け られた領域は、特定の関数の「スタックフレーム」やその関数の前後 の状況に局所的となる。 alloca()を移植性が高いように書くことはできないし、スタックのな いマシン上に実装することは難しい。これを使うことは、戻り値を直 接別の関数に渡す場合に(すぐに思いつくような実装では、スタック に基づくマシンでは必ず失敗する)、たとえば fgets(alloca(100),100, stdin)のような式で問題を招く。 これらの理由により、alloca()は便利にみえるが、移植性が高く なければならないプログラムでは使うことはできない。 質問7.22も参照のこと。 References: Rationale Sec. 4.10.3. 8章 文字と文字列 8.1: どうして、 strcat(string, '!'); はうまく動かないのか。 A: 文字(character)と文字列(string)には大きな違いがある。strcat() は文字列を連結する。 Cの文字は文字集合での値に対応する小さな整数で表現される(以下の 質問8.6を参照)。文字列は文字の配列で表現される。そして普通は配 列の最初の文字を指すポインタを操作する。文字が来ること期待され ているところに文字列を使うことも、文字列が来ることを期待されて いるところに文字を使うことも間違いである。文字列に!を連結する には、 strcat(string, "!"); を使う。 質問1.32,7.2,16.6を参照のこと。 References: CT&P Sec. 1.5 pp. 9-10. 8.2: 文字列が、ある値と一致するかどうか調べるプログラムを書いている。 なぜ以下のコードではうまくいかないのか。 char *string; ... if(string == "value") { /* 文字列が"value"と一致した */ ... } A: Cの文字列は文字の配列で表現される。またCは行列を全体として操作 (代入、比較など)することはない。上のコードの==演算子は2つのポ インタを比較している。つまりポインタ変数stringと文字列 "value"へのポインタが等しいかどうか調べる。ということは両者 が同じ場所を指しているかどうか調べている。たぶん違うところを指 しているので比較が成功することはない。 2つの文字列を比較するには、ライブラリ関数strcmp()を使うのが普 通である。 if(strcmp(string, "value") == 0) { /* 文字列が"value"と一致した */ ... } 8.3: どうして、 char a[] = "Hello, world!"; と書けるのに、 char a[14]; a = "Hello, world!"; と書けないのか。 A: 文字列は配列で、配列に直接は代入できない。代わりにstrcpy()を使 う。 strcpy(a, "Hello, world!"); 質問1.32, 4.2, 7.2も参照のこと。 8.6: 文字に対応する数値(文字集合)の値や、その逆はどうやって求めれば よいか。 A: Cで文字は、その値に対応する(そのマシンが使っている文字集合の)小 さな整数で表現される。だから変換ルーチンは必要ない。文字を持っ ているということは、その値を持っているということである。 8.9: コンパイラが調子悪い。sizeof('a')の値が1ではなくって2になって いる(sizeof(char)の値と違う)。 A: 知らなくて驚くかもしれないが、Cの文字定数はint型を持つ。だから sizeof('a')はsizeof(int)である(ただしC++では話が違う)。質問7.8も 参照のこと。 References: ANSI Sec. 3.1.3.4; ISO Sec. 6.1.3.4; H&S Sec. 2.7.3 p. 29. 9章 ブール数 9.1: ブール値をC言語で扱うのに適切なデータ型は? なぜブール値を扱う データ型が、標準で用意されていないのか。真と偽を表わすのに、 #defineを使うべきか列挙体を使うべきか。 A: C言語は標準のブール型を用意していない。これは1つにはブール型の データ型を選ぶことは空間と時間を天秤にかけることであるからであ る。どちらを選択するかはプログラマにまかせるのが一番である。 (intをブール型に選べば速いだろうし、charをブール型に選べばデー タの保存領域の節約になる。けれどintより小さいデータ型を使うと、 intに変換したりintから変換するのでコードが大きくなったり遅くな るかもしれない。) #defineを使うか列挙体を使うかはどっちでもよいことで、とくに興 味を引くようなことはない(質問2.22と17.10を参照のこと)。プログ ラムやプロジェクトで首尾一貫している限り、以下のどれを使っても かまわない #define TRUE 1 #define YES 1 #define FALSE 0 #define NO 0 enum bool {false, true}; enum bool {no, yes}; あるいは生の1と0を使うのもいい(変数の値を表示するときに列挙体 の名前で表示してくれるデバッガを使っているのであれば、列挙体の ほうがいいかもしれない)。 以下のようなちょっと違った形を好む人もいる。 #define TRUE (1==1) #define FALSE (!TRUE) あるいは"補助の"マクロ、例えば #define Istrue(e) ((e) != 0) を定義する人もいる。どちらにしても大した効果はない(以下の質問 9.2を参照。また質問5.12と10.2も参照のこと)。 9.2: TRUEを1に#defineすることは危険ではないのか。なぜなら、C言語で は0でない値はすべて"真"と考えられるから。組み込みのブール値を 返す演算子や関係演算子が1以外の値を"返したら"どうするのか。 A: C言語では、どんな非0の値も真と考えられることは真実である。しか しこのことは「入力においてのみ」、すなわちブール値がくることを 期待されているところでだけなりたつ。組み込みの演算子によってブー ル値が産み出されるときは、1か0であることが保証されている。よっ てテスト if ((a == b) == TRUE) は(TRUEが1であるかぎり)期待したとおりの結果を返す。しかしバカ げたことである。一般にTRUEやFALSEを相手に明示的にテストするこ とは望ましくない。なぜならライブラリ関数の中には(有名なのは isupper()、isalpha()など)条件が成立したときに非0の値を返すが、 その値は必ずしも1ではないものがある(さらに、もし君が「if((a == b) == TRUE)」が「if(a == b)」の改良版であると信じるのなら、な ぜそこで止めるのか。なぜ「if (((a == b) == TRUE) == TRUE)」を 使わないのか)。おおまかな目安としては、TRUEとFALSEを(あるいは 似た物を)ブール値をあらわす変数に代入する際や、関数の引数、ブー ル値を返す関数の戻り値としてだけ使うこと。これらのマクロを決し て比較に使ってはいけない。 プリプロセッサのマクロのうち、TRUEやFALSE(もちろんNULLも)は コードの可読性を上げるのに使うのであって、あらわす値が変わる可 能性があるから使うのではない(質問5.3と5.10も参照のこと)。 一方、ブール値の取る値や、定義の仕方はどう見ても混乱を招く可能 性がある。プログラマの中にはTRUEとFALSEマクロは混乱の度合を 増すだけだと考えている人もいる。 References: K&R1 Sec. 2.6 p. 39, Sec. 2.7 p. 41; K&R2 Sec. 2.6 p. 42, Sec. 2.7 p. 44, Sec. A7.4.7 p. 204, Sec. A7.9 p. 206; ANSI Sec. 3.3.3.3, Sec. 3.3.8, Sec. 3.3.9, Sec. 3.3.13, Sec. 3.3.14, Sec. 3.3.15, Sec. 3.6.4.1, Sec. 3.6.5; ISO Sec. 6.3.3.3, Sec. 6.3.8, Sec. 6.3.9, Sec. 6.3.13, Sec. 6.3.14, Sec. 6.3.15, Sec. 6.6.4.1, Sec. 6.6.5; H&S Sec. 7.5.4 pp. 196-7, Sec. 7.6.4 pp. 207-8, Sec. 7.6.5 pp. 208-9, Sec. 7.7 pp. 217-8, Sec. 7.8 pp. 218-9, Sec. 8.5 pp. 238-9, Sec. 8.6 pp. 241-4; "亀がアキレスに言ったこと". 9.3: pがポインタだとしてif(p)は正しい条件か。 A: 正しい。質問5.3を参照のこと。 10章 Cプリプロセッサ 10.2: 見て見て下の気のきいたマクロ。 #define begin { #define end } どう思う。 A: ゲーッ。17章も参照のこと。 10.3: 2つの値を交換する汎用のマクロは。 A: この質問に確かな解答はない。もしも値が整数なら有名な排他論理和 を使った技を使うことができる。しかし、この技も浮動小数点表示の 数やポインタには使えない(整数の場合も同一の変数を2つの引数 として指定することはできない)。また整数型の"誰でもわかる"極端 に詰め込んだコードa^=b^=a^=bも、副作用が複数回起こることにより 厳密にいえば文法違反である)。もしマクロを任意の型の値に使いた いなら(普通はこれが目的である)、一時変数を使うことはできない。 なぜなら、どの型の一時変数が必要かわからないからである(もし一 時変数が使えたとしても、変数の名前の付けかたに苦労するだろう)。 標準Cはtypeof演算子を用意していない。 一番の万能の解決方法は、マクロを使うことを考えないことだ。ただ し型を渡すために第3の引数を渡すのが面倒でないなら話は別だ。 10.4: 複数の文(multi-statement)からなるマクロを書くにはどうすれば よいか。 A: たいてい目標は、関数を呼び出し1つからなる1つの文のように呼べるマ クロを書くことである。ということは使う人が最後のセミコロンを自 分で付けるから、マクロ本体にはセミコロンを付けてはいけないことに なる。マクロ本体は、単に括弧{}でくくった複数の文であってはな らない。なぜならマクロが(見た目は一つの文として、かつ余計なセ ミコロンを付けて呼ばれたときに)、else節を持つif/else文のifが成 立したときの分岐に使われたときに文法エラーとなる。 昔からの解決方法は以下に示すものである。 #define MACRO(arg1, arg2) do { \ /* declarations */ \ stmt1; \ stmt2; \ /* ... */ \ } while(0) /* (後ろの;は無し) */ 使う人がセミコロンを付けたときに、上記のマクロはどこで使われよ うと1つの文となる(最適化を行うコンパイラは、必ず偽になる"死ん だ"テストや定数0相手の条件成立に対する分岐を取り去るだろう。た だしlintは文句を付けるかもしれない)。 マクロの中のすべての文が、宣言もループも持たない単純な式なら、 丸括弧()でくくってコンマ演算子で区切った式を使うことができる (例としては、質問10.26の最初のDEBUG()マクロを参照のこと)。この 方法を使えば値を"返す"こともできる。 References: H&S Sec. 3.3.2 p. 45; CT&P Sec. 6.3 pp. 82-3. 10.6: 初めて、プログラムを複数のソースファイルに分けている。何を.cファ イルに入れ、何を.hに入れたらいいか悩んでいる。(そもそも「.h」っ て何?) A: 一般に、以下のものはヘッダ(.h)に入れる。 マクロの定義(プリプロセッサの#define) 構造体、共用体、列挙体の宣言 typedefの宣言 外部関数の宣言(質問1.11も参照のこと) グローバル変数の宣言 定義や宣言を複数のファイルで共有するときは、そういう定義や宣言 をヘッダファイルに入れることはとくに大事である。(絶対に外部 関数のプロトタイプを.cファイルに入れてはいけない。質問1.7も参 照のこと) 一方、宣言や定義をあるソースファイルの専用にしたいなら、そのソー スファイルの中に入れておいてもかまわない。 質問1.7と10.7も参照のこと。 References: K&R2 Sec. 4.5 pp. 81-2; H&S Sec. 9.2.3 p. 267; CT&P Sec. 4.6 pp. 66-7. 10.7: ヘッダが、別のヘッダを#includeすることは容認されているのか。 A: これは書き方に関する質問であり、この質問に関する議論は盛り上が る。多くの人が、入れ子の#includeは止めたほうがいいと信じている。 権威あるIndian Hillスタイルガイド(質問17.9参照)は、以下の理由 により入れ子はよくないとしている。関連する定義を捜しにくする。 ファイルが二回以上#includeされると多重宣言エラーとなる。 Makefileの保守が面倒になる。一方#includeを入れ子にするとヘッダ ファイルをモジュールを組み上げるように使うことができる(ヘッダ ファイルが必要なファイルを#includeする。使い手はいちいち #includeしてまわらなくてすむ。いちいち#includeしてまわるのは手 に負えなくなって頭痛の種となる)。grep(やタグファイルのようなツー ル)があれば、定義がどこにあるのか見つけるのを楽になる。有名な トリック #ifndef HEADER_FILE_NAME #define HEADER_FILE_NAME …ヘッダファイルの中身… #endif は(各ヘッダファイルごとに異なるマクロ名を用意して、#ifndefでく くる)ヘッダファイルの「べき等(A * A = A)」を可能にする(2度以上 読み込まれても問題が発生しない)。こうすれば何回#includeしても 問題ない。Makefileを保守するための自動化ツール(どっちにしても、 この手のツールはプロジェクトが大きくなれば絶対必要となる。質問 18.1を参照のこと)は、入れ子になった#includeファイルの依存関係 をうまく作り出す。質問17.10を参照のこと。 References: Rationale Sec. 4.1.2. 10.8: ヘッダ(#include)ファイルを捜すのに、どこを捜しにいくのか。 A: 動作の細かいところは処理系依存である(ということは文章にしてな ければならないということを意味する。質問11.33参照)。通常は、<> の構文を持つヘッダファイルなら1つ以上ある標準の場所を探しに行 く。""の構文を持つヘッダファイルなら"現在のディレクトリ(カレン トディレクトリ:current directory)"を最初に探して、(見つからな かったら) <>のときと同じ標準の場所に探しに行く。 昔から(特にUnixのコンパイラでは)、現在のディレクトリとは #includeが書かれたファイルの存在するディレクトリのことである。 しかしながらコンパイラによっては、現在のディレクトリ(そういう ものがあったとして)とはコンパイラを呼び出したディレクトリであ ることもある。コンパイラに付いてきた資料を読むこと。 References: K&R2 Sec. A12.4 p. 231; ANSI Sec. 3.8.2; ISO Sec. 6.8.2; H&S Sec. 3.4 p. 55. 10.9: ファイルの最初の最初の宣言で奇妙な構文エラーが出た。よさそうに 見えるのに。 A: #includeしたファイルの最後の宣言の終わりにセミコロン を付けるのを忘れたのだろう。質問2.18, 11.29, 16.2aも参照のこと。 10.11: 私の使っているシステムにはが見当たらない。だれか 送ってくれないだろうか。 A: 標準ヘッダは、君が使っているコンパイラ、オペレーティングシステ ム、プロセッサに適切な定義を与えるような形で存在している。他の 人のヘッダファイルを引っ張ってきて動くことを期待することはでき ない。もちろん相手が全く同じ環境を使っている場合は別である。コ ンパイラのベンダーになぜそのファイルが供給されなかったか尋ねて みること(あるいは代わりを送ってくれるように依頼してみること)。 10.12: 文字列を比較するプリプロセッサの#if式はどうすれば作れるか。 A: 直接には不可能である。プリプロセッサの#ifの計算のところには整 数しか使えない。わかりやすいラベルを整数定数に#defineして、そ れらのラベルを使った条件を実装するしかない。 質問20.17も参照のこと。 References: K&R2 Sec. 4.11.3 p. 91; ANSI Sec. 3.8.1; ISO Sec. 6.8.1; H&S Sec. 7.11.1 p. 225. 10.13: #ifの中でsizeof()は使えるか。 A: 使えない。前処理はコンパイルの早い段階で、つまりデータ型の名前 を構文解析する前で実行される。利用可能ならANSIの内に あらかじめ定義された定数を使うか、"構成(configure)"スクリプト を使うこと(もっとよいのはデータ型の大きさに依存しないようなコー ドを書くことである)。 References: ANSI Sec. 2.1.1.2, Sec. 3.8.1 footnote 83; ISO Sec. 5.1.1.2, Sec. 6.8.1; H&S Sec. 7.11.1 p. 225. 10.14: #ifdefを#defineの行に使って、あるものを2つのまったく異なった 風に定義することができるか。 A: できない。「プリプロセッサをプリプロセッサにかけることはで きない」といったところか。 できるのは、#ifdefの設定によってまったく別個の2つの#defineの うちの1つを使うことである。 References: ANSI Sec. 3.8.3, Sec. 3.8.3.4; ISO Sec. 6.8.3, Sec. 6.8.3.4; H&S Sec. 3.2 pp. 40-1. 10.15: typedefで使える#ifdefのようなものがないか。 A: 残念ながら存在しない。あるtypedefが定義されたかどうかをマクロ (例 MY_TYPE_DEFINED)を使って記録しなければならない。(質問10.13 も参照のこと。) References: ANSI Sec. 2.1.1.2, Sec. 3.8.1 footnote 83; ISO Sec. 5.1.1.2, Sec. 6.8.1; H&S Sec. 7.11.1 p. 225. 10.16: プリプロセッサの#ifを使って、マシンがビッグエンディアンかリ トルエンディアンかを知ることができるか。 A: たぶんできない(プリプロセッサでの演算はすべてlongで行われ、 そこにはアドレスの概念はない)。本当にマシンのエンディアンを明 示的に知りたいのか。エンディアンを気にしないコードを書くほうが よい。質問20.9も参照のこと。 10.18: こんど押し付けられたコードは#ifdefだらけで私の趣味にあわない。 このコードを条件コンパイルの一組だけ残して、cppを通すことなく、 かつ#includeや#defineは展開しないで前処理する方法があるか。 A: 世の中にはunifdef、rmifdef、scpp("(選択式Cプリプロセッサ) selective C preprocessor")というプログラムが出まわっていて、こ れがまさに上に書かれている機能を持つ。質問18.16を参照のこと。 10.19: (訳注:__DATE__や__TIME__のような)あらかじめ定義されたマクロの 一覧を得るにはどうすればよいか。 A: 何度も必要になるにもかかわらず標準の方法というのは存在しない。 コンパイラ附属の資料が役に立たないなら、一番目的にかなった方法 は、たぶんUnixのstrings(1)かなんかを使って、コンパイラやプリプ ロセッサの実行ファイルから文字列を拾いだすことであろう。従来 の、システムごとで予め#defineされた識別子(例:unix) は、規格違 反であるから(これらの識別子はユーザーの名前空間(name space)と 衝突する)、削除されたり名前を変更されたことに注意すること。 10.20: 識別子をマクロを使って作り出す、以下の古いコードを使っていた。 #define Paste(a, b) a/**/b でももう動かなくなってしまった。 A: これはCの歴史の初期のコンパイラ(有名なのはJohn Reiserが書いた もの)のいくつかが持っていた非公開の機能であった。つまりコメン トはまるっきり消えてなくなってしまうので、トークンの連結に使え るというものがあった。ANSI規格は(K&R1と同様)、コメントは空白に 置き換えられると明言している。しかし、トークンを連結したいとい う要求が出され、その要求は現実問題に即したものであったから、トー クンを連結する演算子##をANSI規格は適切に定義して導入した。##は 以下のように使うことができる。 #define Paste(a, b) a##b 質問11.17も参照のこと。 References: ANSI Sec. 3.8.3.3; ISO Sec. 6.8.3.3; Rationale Sec. 3.8.3.3; H&S Sec. 3.3.9 p. 52. 10.22: なぜ以下のマクロ #define TRACE(n) printf("TRACE: %d\n", n) で、「文字列リテラルの内部のマクロ置換」という警告が出るのか。 TRACE(count); を printf("TRACE: %d\count", count); と展開してるようだ。 A: 質問11.18を参照。 10.23-4: マクロの展開で文字列リテラルの内側で引数を使いたい。どうすれば よいか。 A: 質問11.17と11.18を参照。 10.25: コンパイル時に凝った処理をしたいが、どうやってcppにやらせたら よいかわからない。 A: cppは汎用のプリプロセッサとしては作られていない。(別プログラム として使うことができることさえ保証されていない)。不似合いなこ とをcppにさせるよりは、特別な用途のための前処理を行うツールを 自分で作ったほうがよい。make(1)のような道具を使えば、自動的に 仕事をさせることができる。 もしC言語以外のものを前処理することを考えているのであれば、汎 用のプリプロセッサを使うことを考えること。(たいていのUnixシス テムで使える古くから存在するツールとしてはm4がある)。 10.26: 可変個の引数を取るcppのマクロをどうやって書けばよいか。 A: 有名なトリックは、引数を1つ取るマクロを定義し、使うときにも引 数を括弧でくくるというものである。そうすればマクロを展開したも のは括弧も何もかもprintfのような関数の引数リストになる。 #define DEBUG(args) (printf("DEBUG: "), printf args) if(n != 0) DEBUG(("n is %d\n", n)); 明らかな欠点は、使い手が余計な括弧を付けることを覚えておかな ければならないことである。 gccは拡張機能として関数型のマクロが可変個数の引数を取ることを 許している。しかしこれは標準ではない。別の方法は、引数の数に応 じて別のマクロ(DEBUG1、DEBUG2など)を用意することや、以下のよう にコンマを使うことである。 #define DEBUG(args) (printf("DEBUG: "), printf(args)) #define _ , DEBUG("i = %d" _ i) それよりは適切に定義された方法で可変個の引数を扱う特製の関数を 作るほうがよい。質問15.4と15.5を参照のこと。(マクロ置換が必要 なら、関数と#define printf myprintfのような関数型でないマクロ を使う。)