PRE10-C. 複数の文からなるマクロは do-while ループで包む
複数の文を一つのグループとして実行するために、マクロがよく使われる。
この手の仕事には、一般にインライン関数の方が適している(「PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う」を参照)。しかし、インライン関数を使えない場面もある(たとえば、異なる型の変数に対する演算を行うときなど)。
マクロの中に複数の文が含まれている場合、これらの文は do-while
ループで構文的にひとまとめにしておくべきである。そうすることで、if
節の中など、単一の文や文のブロックが期待される箇所において、安全にマクロを使うことができる。あるいは、if
文、for
文、while
文において、本体が単一の文であっても波括弧を使用することで、マクロ中の複数の文は
違反コード
以下の違反コード例では、複数の文がひとまとめにされていない。
/* * 2つの値を入れ替える * 変数 tmp はあらかじめ定義されていること */ #define SWAP(x, y) \ tmp = x; \ x = y; \ y = tmp
このマクロは普通の文中では正しく展開されるが、if
文の中の then
節では正しく展開されない。
int x, y, z, tmp; if (z == 0) SWAP(x, y);
上記のコードは次のように展開されるが、もちろんこれはプログラマの意図とは異なっている。
int x, y, z, tmp; if (z == 0) tmp = x; x = y; y = tmp;
さらに、このマクロは「PRE02-C. マクロ置換リストは括弧で囲む」にも違反している.
違反コード
以下の違反コード例では、複数の文を正しくひとまとめにしていない。
/* * 2つの値を入れ替える * 変数 tmp はあらかじめ定義されていること */ #define SWAP(x, y) { tmp = (x); (x) = (y); (y) = tmp; }
このマクロは正しく展開されない場合がある。例えば次のコード例のように、if
文に2つの分岐を設けようとする場合である。
if (x > y) SWAP(x, y); /* 分岐 1 */ else do_something(); /* 分岐 2 */
マクロ展開後のコードは、1つの分岐のみを持つ if
文として解釈される。
if (x > y) { /* 分岐が1つだけの if 文!!! */ tmp = x; /* 分岐はこのブロックだけから */ x = y; /* 成るように見える */ y = tmp; } ; /* 空の文 */ else /* エラー!!! "else の前でパースエラー" */ do_something();
問題はブロックの後ろにあるセミコロン(;
)である。
適合コード
マクロの本体を do-while
ループの中に包み込むことでこの問題を緩和することができる。
/* * 2つの値を入れ替える * 変数 tmp はあらかじめ定義されていること */ #define SWAP(x, y) \ do { \ tmp = (x); \ (x) = (y); \ (y) = tmp; } \ while (0)
do-while
ループは常に一度だけ実行される。
このコード例でも、マクロ引数が2回評価されるため、「PRE12-C. 安全でないマクロを定義しない」に違反している。実引数には単純な左辺値が渡されることが想定されている。
リスク評価
マクロの本体を適切に do-while
ループで包んでおかないと、予期せぬ動作、あるいは原因究明が困難な動作を引き起こすおそれがある。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
PRE10-C |
中 |
中 |
低 |
P12 |
L1 |
自動検出(最新の情報はこちら)
ツール | バージョン | チェッカー | 説明 |
---|---|---|---|
Axivion Bauhaus Suite |
6.9.0 |
CertC-PRE10 |
|
Klocwork |
2018
|
MISRA.DEFINE.BADEXP |
|
LDRA tool suite |
9.7.1
|
79 S | Enhanced enforcement |
Polyspace Bug Finder |
R2020a |
CERT C: Rec. PRE10-C | Checks for macros with multiple statements (rule fully covered) |
PRQA QA-C |
9.7
|
3412, 3458 |
実装済み |
関連するガイドライン
ISO/IEC TR 24772:2013 | Pre-processor Directives [NMP] |
参考資料
Linux Kernel Newbies FAQ | FAQ/DoWhile0 |
翻訳元
これは以下のページを翻訳したものです。
PRE10-C. Wrap multistatement macros in a do-while loop (revision 104)