char c;A:
while((c = getchar()) != EOF) ...
getchar()の戻り値を格納する変数はintでなければならない。 getchar()は、文字型のあらゆる値を返すだけでなくEOFも返す。 getchar()の戻り値をcharで渡すと、普通の文字が返ってきたのにEOF と誤解されたり、EOFが他の値に変えられて(特にデータ型charが符号 なしの場合)、いつになってもEOFが出てこないかもしれない。
References:
K&R1 Sec. 1.5 p. 14; K&R2 Sec. 1.5.1 p. 16; ANSI
Sec. 3.1.2.5, Sec. 4.9.1, Sec. 4.9.7.5; ISO Sec. 6.1.2.5,
Sec. 7.9.1, Sec. 7.9.7.5; H&S Sec. 5.1.3 p. 116, Sec. 15.1,
Sec. 15.6; CT&P Sec. 5.1 p. 70; PCS Sec. 11 p. 157.
while(!feof(infp)) {A:
fgets(buf, MAXLINE, infp);
fputs(buf, outfp);
}
Cでは、EOFは入力ルーチンが読もうとしてファイルの終わり (End-Of-File)にたどり着いた後であることを示しているだけである (言い換えればC言語のI/OはPascalのI/Oとは異なる)。たいていは入 力関数(この場合はfgets)の戻り値をチェックすればよい。feof()を 使う必要がまったくない場合が多い。
References:
K&R2 Sec. 7.6 p. 164; ANSI Sec. 4.9.3, Sec. 4.9.7.1,
Sec. 4.9.10.2; ISO Sec. 7.9.3, Sec. 7.9.7.1, Sec. 7.9.10.2; H&S
Sec. 15.14 p. 382.
A:
出力が見えて欲しいところでは、必ず明示的にfflush(stdout)を使っ て出力をはきだすこと。ほっておいてもいくつかの仕組みが、 fflush()を"適切なときに"実行してくれる。しかし、これはstdoutが 端末のときにしか適応されないことが多い(質問12.24も参考のこと)。
References:
ANSI Sec. 4.9.5.2; ISO Sec. 7.9.5.2.
A:
質問19.1を参照のこと。
A:
%%というふうに、%を二回続けて書け。
\%ではうまくいかない。バックスラッシュ\はコンパイラーのエスケー プシーケンスで、printfのエスケープシーケンスは%だ。
質問19.17も参照のこと。
References:
K&R1 Sec. 7.3 p. 147; K&R2 Sec. 7.2 p. 154; ANSI
Sec. 4.9.6.1; ISO Sec. 7.9.6.1.
A:
printf()の%f指定子はfloatの引数にもdoubleの引数にも作用する。" 省略時の引数の格上げ"により(この規則はprintfのような可変数個の 引数を取る関数に、その関数がプロトタイプを持っていてもいなくて も適応される)、データ型floatの変数はdoubleに格上げされる。よっ てprintf()が見るのはdoubleの変数だけである。質問12.13, 15.2も 参照のこと。
References:
K&R1 Sec. 7.3 pp. 145-47, Sec. 7.4 pp. 147-50; K&R2
Sec. 7.2 pp. 153-44, Sec. 7.4 pp. 157-59; ANSI Sec. 4.9.6.1,
Sec. 4.9.6.2; ISO Sec. 7.9.6.1, Sec. 7.9.6.2; H&S Sec. 15.8 pp.
357-64, Sec. 15.11 pp. 366-78; CT&P Sec. A.1 pp. 121-33.
A:
printf("%*d", width, n)が君の望むことをやってくれる。質問12.15 も参照のこと。
References:
K&R1 Sec. 7.3; K&R2 Sec. 7.2; ANSI Sec. 4.9.6.1; ISO
Sec. 7.9.6.1; H&S Sec. 15.11.6; CT&P Sec. A.1.
A:
<locale.h>にあるルーチンでこれらの操作に対応するものも出始めた。 しかし上のどちらの仕事についても標準のルーチンというのは存在し ない。(printf()が、その土地の慣習にしたがうことといえば小数点 の表示に使う文字を変更することだけである。)
References:
ANSI Sec. 4.4; ISO Sec. 7.4; H&S Sec. 11.6 pp. 301-4.
A:
scanf()は、値を書き込む変数へのポインターを必要とする。 scanf("%d", &i);と書かなければならない。
double d;A:
scanf("%f", &d);
printf()と違ってscanf()ではdoubleには%lfを、floatには%fを使う。 質問12.9も参考のこと。
A:
できない。scanf()の書式文字列にアスタリスクを書くと、代入を抑 制することとなる。ANSIの文字列作成(stringizing)や文字列連結の 機能を使って、ほとんど同じことができる。scanf()の書式文字列を その場で作るのもいい。
A:
知らなくて驚くだろうけど、scanfの書式文字列で\nは改行を意味す るのではなく、空白が続くかぎり読んでは捨てることを意味している。 質問12.20も参照のこと。
References:
K&R2 Sec. B1.3 pp. 245-6; ANSI Sec. 4.9.6.2; ISO
Sec. 7.9.6.2; H&S Sec. 15.8 pp. 357-64.
A:
scanfの%dはおしりの改行は食べてくれない。もし入力の数のすぐ後 ろに改行が来たら、その改行はgets()の入力としての条件を満足する。
一般に、scanf()の呼び出しと、gets()(その他の入力ルーチンも)の 呼び出しを混ぜて使ってはいけない。scanf()の改行の扱いが変わっ ているので必ずやっかいなことになる。scanf()ですべて読み込むか、 scanf()を使わないかのどちらかである。
References:
ANSI Sec. 4.9.6.2; ISO Sec. 7.9.6.2; H&S Sec. 15.8
pp. 357-64.
A:
scanf()が数字を変換しようとするときには、数字でない文字に出く わすと変換を終了し、その上それらの文字を入力ストリームに放置す る。したがって他の手段を取らないと、数字でない入力が思いがけな いところで出てくるとscanf()が何度も"ジャム"ってしまう。scanf() は問題となる文字を通り過ぎて、その後ろにある正しいデータに行く ことはない。もしユーザーがscanf()の数値のフォーマットである%d や%fなどに対して'x'のような文字を入力したとすると、単にプロン プトをまた出して同じscanf()を呼び出すようなコードは、即座に同 じ'x'に出くわすことになる。
質問12.20も参照のこと。
References:
ANSI Sec. 4.9.6.2; ISO Sec. 7.9.6.2; H&S Sec. 15.8
pp. 357-64.
A:
scanf()には数多くの問題がある。質問12.17, 12.18, 12.19を参照の こと。さらにscanf()の%sフォーマットはgets()が抱えているのと(質 問12.23を参照)同じ問題を抱えている。つまり受け取りに使うバッファ があふれる心配がないと保証するのは難しい。
もっと一般的な話として、scanf()はかなりしっかりした構造を持っ た、書式にのっとった入力を対象に設計されている(実際scanfという 名前は「scan formatted(書式を持ったものを走査する)というところ から来ている」)。注意していれば、処理が成功したかどうかわかる。 しかし、どこで失敗したかは大体のところしか分からないし、どうい う風にとか何故かはぜんぜんわからない。scanf()を使った場合エラー からのまっとうな復帰はほとんど不可能である。たいていは1行丸々 (fgets()かなにかで)読み込んで、sscanf()かその他の技を使って解 釈するほうがずっと簡単である。(strtol()とかstrtok()とかatoi() といったルーチンも役に立つことが多い。質問13.6も参照のこと。) どうしてもsscanf()を使わなければならなければ、戻り値を調べてこ ちらが期待した数だけ項目が見つかったことを確認するのを忘れては いけない。
References:
K&R2 Sec. 7.4 p. 159.
A:
この2つの鋭い質問には立派な解答は(まだ)ない。ないということは、 これまでのstdioライブラリーの最大の欠陥をたぶん表わしている。
書式文字列が既知で結構単純なときは、毎回特別な方法でバッファの 大きさを予想することができる。書式が一つか二つの%sからできてる ときは、固定の文字の部分は自分で数えて(またはかわりにsizeofに 数えさせて)、挿入される文字列の分はstrlen()を呼んで数えさせ、 結果を加える。%dがどれくらいの大きさを占めるかは、以下のような コードで安全のため大きめに見積もることができる。
#include <limits.h>(上のコードは8進数表現の数を表わすのに必要な文字の数を計算する ものである。10進数表現なら必ず同じかより小さい領域で済む。)
char buf[(sizeof(int) * CHAR_BIT + 2) / 3 + 1 + 1];
sprintf(buf, "%d", n);
書式文字列がもっと複雑だったり実行時までわからない場合は、どれ くらい大きなバッファが必要か予想することはsprintf()を改めて実 装するのと同じくらい困難となり、それだけ間違いを犯しやすくなる (したがって勧めない)。最後の手段として時々提案されるのは fprintf()を使って同じテキストをビットバケツや一時ファイルに書 き込んで、fprintf()の戻り値やファイルの大きさを見ることである (ただし質問19.12も参照のこと)。
バッファがそんなに大きくないかもしれない場合は、バッファがあふ れないとかメモリの別の部分を上書きしないと保証されない限り、 sprintf()を呼びたくないだろう。いくつかのstdio(GNUや4.4bsdの) は名前から機能がわかるsnprintf()関数を用意している。これは以下 のように使う。
snprintf(buf, bufsize, "You typed \"%s\"", answer);ANSI/ISO Cの将来の改訂がこの機能を取り入れることを願うばかりで ある。
References:
Rationale Sec. 4.9.7.2; H&S Sec. 15.7 p. 356.
A:
getsでは、これから読もうとしているバッファの大きさを指定できな いので、バッファがあふれてしまうことを防ぐことができない。質問 7.1の、gets()の代わりにfgets()を使う方法を説明したコードを参考 のこと。
A:
stdioパッケージの多くの実装はstdoutの先が端末のときは振る舞い を少し変える。こういう実装では、出力先を判断するためにstdoutの 先が端末でないときは必ず失敗する操作(これがENOTTYと設定する)を おこなう。よって出力の操作が完全に成功するにもかかわらずerrno の値はENOTTYとなる。(プログラムがerrnoの中身を調べることは、エ ラーが報告されたときだけ意味があることに注意。)
References:
ANSI Sec. 4.1.3, Sec. 4.9.10.3; ISO Sec. 7.1.4,
Sec. 7.9.10.3; CT&P Sec. 5.4 p. 73; PCS Sec. 14 p. 254.
A:
fgetpos()とfsetpos()は特別なtypedefであるfpos_tを、ファイル内 でのオフセット(位置)を表わすのに使う。このtypedefで隠したデー タ型を適切に選べば、オフセットの大きさを好きなように選ぶことが できる。したがってfgetpos()とfsetpos()でどんなに巨大なファイル でも扱える。一方ftell()とfseek()はオフセットを表わすのにlong intを使う。したがってlong intで表わすことのできるオフセットに 限定されてしまう。
References:
K&R2 Sec. B1.6 p. 248; ANSI Sec. 4.9.1,
Secs. 4.9.9.1,4.9.9.3; ISO Sec. 7.9.1, Secs. 7.9.9.1,7.9.9.3;
H&S Sec. 15.5 p. 252.
A:
fflush()は、出力ストリームにしか定義されていない。fflushの "flush(押し流す)"は、バッファリングされた文字の(捨てるのではな く)書き込みを達成することを意味している。読まれてない入力を捨 てることは、入力ストリームをfflushするということと同じ意味では ない。stdioの入力バッファの読まれていない入力を捨てる一般的な 方法はないし、そんなことをするだけでは充分でない。読まれていな い文字は、O/Sレベルのバッファにもため込まれている可能性がある。
A:
書く前にfseek()を必ず呼ぶこと。上書きしようとしている文字列の 先頭にシークして戻すことが必要であるし、読み書き両方可能の「+」 モードでデータを読むときと書くときの間にはfseek()かfflush()が 必要である。元々あった文字と同じ個数しか上書きできないことも忘 れてはいけない。質問19.14も参照のこと。
References:
ANSI Sec. 4.9.5.3; ISO Sec. 7.9.5.3.
A:
freopen()を使え(ただし以下の質問12.34を参照のこと)。
References:
ANSI Sec. 4.9.5.4; ISO Sec. 7.9.5.4; H&S Sec. 15.2.
A:
よい方法は存在しない。もし行ったり来たりしたいのであれば、最高 の方法はfreopen()なんて使わないことである。自分で明示的に定義 した出力(あるいは入力)ストリーム変数を使うこと。そうすれば思う ままに出力(または入力)を割り当てて、しかも元のstdout(あるいは stdin)を壊さなくてすむ。
A:
バイナリーデータを読み込むときには、fopen()を呼ぶ際に、テキス トファイルの変換が実行されないよう、「rb」モードを指定すること が必要である。同じように、バイナリーファイルを書き込むときは、 「wb」を使わなければならない。
テキストとバイナリーはファイルをオープンするときに区別される。 いったんファイルを開いたら、ファイルにどちらのI/Oコールをして いるかは関係ない。質問20.5も参照のこと。
References:
ANSI Sec. 4.9.5.3; ISO Sec. 7.9.5.3; H&S Sec. 15.2.1
p. 348.