JPCERT コーディネーションセンター

FIO13-C. 読み取った一文字以外は押し戻さない

C99 のセクション 7.19.7.11 では、ungetc() を以下のように定義している[ISO/IEC 9899:1999]。

ungetc 関数は、引数 c で指定される文字を(unsigned char 型に変換して) stream が指す入力ストリームに押し戻す。その後の同一ストリームに対する読取りの際には、押し戻された文字を逆順で返す。押し戻した文字を読み取る前に(同一ストリームに対する)ファイル位置付け関数(fseekfsetpos、又は rewind)の呼出しが成功すると、押し戻されていた文字を捨てる。ungetc 関数は、ストリームに対応している外部記憶を変化させない。

どの処理系でも1文字の押戻しを保証する。

ゆえに、同一ストリームに対して ungetc() を 2 回以上呼び出す場合は、続けて呼び出さずに、読み取り関数やファイル位置付け関数を先に呼び出す必要がある。このようにすることで、ungetc() によって押し戻されている文字は高々一文字までとなる。

同様に、ungetwc() に対しては、C99 は 1 つのワイド文字の押し戻しだけを保証している(セクション 7.24.3.10)。ゆえに、同一ストリームに対して ungetwc() を 2 回以上呼び出す場合は続けて呼び出さずに、読み取り関数やファイル位置付け関数を先に呼び出す必要がある。このようにすることで、ungetwc() によって押し戻される文字は高々一文字までとなる。

違反コード

以下のコード例は、fp が参照しているストリームに対して複数の文字を押し戻している。

FILE *fp;
char *file_name;

/* file_name を初期化 */

fp = fopen(file_name, "rb");
if (fp == NULL) {
  /* エラー処理 */
}

/* データの読み取り */

if (ungetc('\n', fp) == EOF) {
  /* エラー処理 */
}
if (ungetc('\r', fp) == EOF) {
  /* エラー処理 */
}

/* 続行 */
適合コード

複数の文字を ungetc() によって押し戻す必要がある場合は、ungetc() を使用して押し戻すのではなく、データの読み取りの前後に、fgetpos()fsetpos() を使用するべきである。以下の解決法は、入力の位置を特定できる場合に限り適用できる点に注意。

FILE *fp;
fpos_t pos;
char *file_name;

/* file_name を初期化 */

fp = fopen(file_name, "rb");
if (fp == NULL) {
  /* エラー処理 */
}

/* データの読み取り */

if (fgetpos(fp, &pos)) {
  /* エラー処理 */
}

/* データの読み取り */

if (fsetpos(fp, &pos)) {
  /* エラー処理 */
}

/* 続行 */

fsetpos() の前に必ず fgetpos() を呼び出すこと(「FIO44-C. fsetpos() には fgetpos() が返す値を使用する」を参照)。

リスク評価

ungetc()ungetwc() を正しく使用しなかった場合、データが切り捨てられたり失われたりするおそれがある。

レコメンデーション 深刻度 可能性 修正コスト 優先度 レベル
FIO13-C P4 L3
自動検出

Compass/ROSE はこのレコメンデーションの単純な違反を検出できる。特に、同じストリームに対して、ファイル位置付け関数またはファイル読み取り関数を 1 回もはさまずに ungetc() 関数が 2 回呼び出されていると、警告を発する。ungetc() がループの内側から呼び出されている場合は、対処できない。

参考情報
翻訳元

これは以下のページを翻訳したものです。

FIO13-C. Never push back anything other than one read character

Top へ

Topへ
最新情報(RSSメーリングリストTwitter