FIO13-C. 読み取った一文字以外は押し戻さない
C99 のセクション 7.19.7.11 では、ungetc() を以下のように定義している[ISO/IEC 9899:1999]。
ungetc 関数は、引数 c で指定される文字を(unsigned char 型に変換して) stream が指す入力ストリームに押し戻す。その後の同一ストリームに対する読取りの際には、押し戻された文字を逆順で返す。押し戻した文字を読み取る前に(同一ストリームに対する)ファイル位置付け関数(fseek、fsetpos、又は 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() がループの内側から呼び出されている場合は、対処できない。
参考情報
- [ISO/IEC 9899:1999] Section 7.19.7.11, "The ungetc function"
翻訳元
これは以下のページを翻訳したものです。
FIO13-C. Never push back anything other than one read character