FIO41-C. 副作用を持つストリーム引数を getc()、putc()、getwc()、putwc() に渡さない
getc()
、putc()
、これらのワイド文字版の getwc()
、putwc()
の呼び出しに副作用を持つストリーム引数を使わないこと。これらの関数が安全でないマクロとして実装されている場合、ストリーム引数は複数回評価される可能性がある。(詳細は PRE31-C. Avoid side effects in arguments to unsafe macros を参照。)
このルールは putc()
の文字引数や putwc
のワイド文字引数には適用されない。これらの文字引数は一度だけ評価されることが保証されている。
違反コード (getc()
)
以下の違反コード例は、ストリーム引数で getc()
関数を呼び出している。getc()
がマクロとして実装されている場合、ファイルが複数回オープンされる可能性がある(「FIO24-C. すでにオープンされているファイルをオープンしない」を参照)。
#include <stdio.h>
void func(const char *file_name) {
FILE *fptr;
int c = getc(fptr = fopen(file_name, "r"));
if (feof(fptr) || ferror(fptr)) {
/* エラー処理 */
}
if (fclose(fptr) == EOF) {
/* エラー処理 */
}
}
この違反コード例は、fopen()
がエラーを返していないかどうかをチェックしていないため、「ERR33-C. 標準ライブラリ関数のエラーを検出し対処する」にも違反している。
適合コード (getc()
)
以下の適合コードでは、getc()
の前に fopen()
を呼び出しており、その返り値のエラーもチェックしている。
#include <stdio.h>
void func(const char *file_name) {
int c;
FILE *fptr;
fptr = fopen(file_name, "r");
if (fptr == NULL) {
/* エラー処理 */
}
c = getc(fptr);
if (c == EOF) {
/* エラー処理 */
}
if (fclose(fptr) == EOF) {
/* エラー処理 */
}
}
違反コード (putc()
)
以下の違反コード例では、putc()
にはストリーム引数が渡されている。putc()
がマクロとして実装されている場合、引数は複数回評価されてしまう可能性がある。
#include <stdio.h>
void func(const char *file_name) {
FILE *fptr = NULL;
int c = 'a';
while (c <= 'z') {
if (putc(c++, fptr ? fptr :
(fptr = fopen(file_name, "w"))) == EOF) {
/* エラー処理 */
}
}
if (fclose(fptr) == EOF) {
/* エラー処理 */
}
}
putc()
マクロがその第2引数を複数回評価しても、?:
演算子が fopen()
の複数回の呼び出しを防止しているため、安全なように見えるかもしれない。しかし、第2引数が複数回評価されるときに fptr
への代入と fptr
の評価が同じタイミングで発生する可能性があるため、未定義動作となる。また、fopen()
の返り値をチェックしていないため、「ERR33-C. 標準ライブラリ関数のエラーを検出し対処する」にも違反している。
適合コード (putc()
)
以下の適合コードでは、putc()
の引数は副作用を持たないものになっている。
#include <stdio.h>
void func(const char *file_name) {
int c = 'a';
FILE *fptr = fopen(file_name, "w");
if (fptr == NULL) {
/* エラー処理 */
}
while (c <= 'z') {
if (putc(c++, fptr) == EOF) {
/* エラー処理 */
}
}
if (fclose(fptr) == EOF) {
/* エラー処理 */
}
}
putc()
は第1引数の評価が一度だけであることを保証しているため、c++
は安全である。
NOTE: このコード例の出力は、実行環境が扱っている文字セットにより異なる。たとえば、ISO-8859 や Unicode など ASCII 由来のコードセットを使用する実行環境で実行した場合、アルファベットの小文字 26 文字が出力される。しかし、Codepage 037 や Codepage 285 など EBCDIC 由来のコードセットを使用する実行環境で実行すると、引用符や記号も出力されるだろう。
リスク評価
副作用のある式を getc()
、putc()
、getwc()
のストリーム引数として使用すると、予期せぬ動作やプログラムの異常終了を引き起こす可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
FIO41-C |
低 |
低 |
中 |
P2 |
L3 |
自動検出
ツール |
バージョン |
チェッカー |
説明 |
---|---|---|---|
Astrée |
20.10
|
stream-argument-with-side-effects | 実装済み |
Helix QAC |
2021.1 |
C5036 C++3225, C++3229 |
|
LDRA tool suite |
9.7.1
|
35 D, 1 Q, 9 S, |
実装済み |
Parasoft C/C++test |
2021.1
|
CERT_C-FIO41-a |
The value of an expression shall be the same under any order of evaluation that the standard permits Don't write code that depends on the order of evaluation of function arguments |
R2021a |
CERT C: Rule FIO41-C | Checks for stream arguments with possibly unintended side effects (rule fully covered) | |
PRQA QA-C |
9.7 |
5036 |
|
PRQA QA-C++ |
4.4 |
3225, 3229 |
|
RuleChecker |
20.10 |
stream-argument-with-side-effects | 実装済み |
関連するガイドライン
Taxonomy |
Taxonomy item |
Relationship |
---|---|---|
CERT C コーディングスタンダード | FIO24-C. すでにオープンされているファイルをオープンしない | Prior to 2018-01-12: CERT: Unspecified Relationship |
CERT C コーディングスタンダード | EXP30-C. 副作用が発生する式の評価順序に依存しない | Prior to 2018-01-12: CERT: Unspecified Relationship |
翻訳元
これは以下のページを翻訳したものです。
FIO41-C. Do not call getc(), putc(), getwc(), or putwc() with a stream argument that has side effects (revision 103)