プログラミング、リファクタリング、そしてすべてにおける究極の疑問: No. 21

その他インテル® DPC++/C++ コンパイラー特集

この記事は、インテル® デベロッパー・ゾーンに公開されている「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* のコード例を参照してください)。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

タイトルとURLをコピーしました