この記事は、インテル® デベロッパー・ゾーンに公開されている「The Ultimate Question of Programming, Refactoring, and Everything」の日本語参考訳です。
21. ファイル終了文字 (EOF) に正しく到達したかチェックする
引き続き、ファイルの操作と EOF について見ていきましょう。ただし、これは全く異なる種類の問題です。通常、ソフトウェアのローカライズ版で発生します。
Computational Network Toolkit プロジェクトから抜粋した以下のコードについて考えてみます。このエラーは、次の PVS-Studio 診断によって検出されます。
V739 EOF should not be compared with a value of the ‘char’ type. The ‘c’ should be of the ‘int’ type. (V739 EOF は ‘char’ 型の値と比較すべきではありません。’c’ は ‘int’ 型であるべきです。)
string fgetstring(FILE* f) { string res; for (;;) { char c = (char) fgetc(f); if (c == EOF) RuntimeError("error reading .... 0: %s", strerror(errno)); if (c == 0) break; res.push_back(c); } return res; }
説明
EOF は次のように定義されています。
#define EOF (-1)
つまり、EOF は int 型の ‘-1’ です。Fgetc() 関数は、int 型の値を返します。具体的には、0 から 255 の範囲の値か、-1 (EOF) を返します。読み取られた値は、char 型の変数に格納されます。このため、値 0xFF (255) の記号は -1 になり、ファイルの終わり (EOF) と同様に処理されます。
拡張 ASCII コードを使用する場合、プログラムが文字記号を正しく処理できないと、エラーになります。
例えば、Windows* 1251 コードページでは、ロシア語の最後のアルファベット文字に含まれる 0xFF コードが、プログラムによってファイル終了文字として解釈されます。
正しいコード
for (;;) { int c = fgetc(f); if (c == EOF) RuntimeError("error reading .... 0: %s", strerror(errno)); if (c == 0) break; res.push_back(static_cast<char>(c)); }
推奨事項
ここでは特に推奨事項はありませんが、EOF に関して広く認識されていない興味深いエラーを紹介したいと思います。
関数が int 型を返す場合、すぐに char 型に変更すべきではないことを覚えておいてください。まず、問題がないことを確認する必要があります。ところで、これと似た memcmp() のケースについては、すでに「2. 0 よりも大きいは 1 を意味しない」で述べました (同セクションの MySQL* のコード例を参照してください)。
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。