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

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

この記事は、インテル® デベロッパー・ゾーンに公開されている「The Ultimate Question of Programming, Refactoring, and Everything」の日本語参考訳です。


20. ファイルの終わり (EOF) チェックだけでは不十分な場合がある

SETI@home プロジェクトから抜粋した以下のコードについて考えてみます。このエラーは、次の PVS-Studio 診断によって検出されます。

V663 Infinite loop is possible. The ‘cin.eof()’ condition is insufficient to break from the loop. Consider adding the ‘cin.fail()’ function call to the conditional expression. (V663 無限ループの可能性があります。’cin.eof()’ 条件はループから抜け出すのには不十分です。条件式に ‘cin.fail()’ 関数呼び出しを追加することを検討してください。)

template <typename T>
std::istream &operator >>(std::istream &i, sqlblob<T> &b) 
{
  ....
  while (!i.eof()) 
  {
    i >> tmp;
    buf+=(tmp+' ');
  }
  ....
}

説明

ストリーム・オブジェクトからデータを読み取る操作は、一見するよりも単純ではありません。ストリームからデータを読み取る場合、プログラマーは通常 eof() メソッドを呼び出して、ストリームの終わりに到達したかどうかをチェックします。ただし、このチェックは十分ではなく、特定の問題につながるデータ読み取りエラーやストリームの整合性エラーを確認できないため、適切ではありません。

注: ここで提供する情報は、入力ストリームと出力ストリームの両方を考慮しています。繰り返しを避けるため、ここでは 1 つのストリームタイプについてのみ述べます。

これはまさに上記のコード例における問題です。データ読み取りエラーが発生した場合、eof() メソッドは常に false を返すため、無限ループになります。さらに、不明な値が tmp 変数に渡されるため、ループで不正なデータが処理されます。

このような問題を回避するため、追加のメソッド bad()fail() を使用してストリームの状態をチェックする必要があります。

正しいコード

ストリームは暗黙的に bool 型にキャストできることを利用します。true 値は、値が正常に読み取られたことを示します。このコードの動作については、Stack Overflow に詳しい説明 (英語) があります。

template <typename T>
std::istream &operator >>(std::istream &i, sqlblob<T> &b) 
{
  ....
  while (i >> tmp) 
  {
    buf+=(tmp+' ');
  }
  ....
}

推奨事項

ストリームからデータを読み取る場合、eof() メソッドだけを使用するのではなく、エラーもチェックするようにします。

bad() メソッドと fail() メソッドを使用して、ストリームの状態をチェックします。bad() メソッドはストリームの整合性をチェックし、fail() メソッドはデータの読み取りエラーをチェックします。

ただし、正しいコード例に示すように bool() 演算子を使用するほうが簡単です。

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

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