PRE12-C. 安全でないマクロを定義しない
安全でない関数形式マクロとは、展開したときに引数を 2 回以上評価する、または 1 回も評価しないものを指す。関数呼び出しでは各引数が必ず 1 回だけ評価されるが、安全でない関数形式マクロではしばしば予期せぬ意外な結果が生じ、検知の難しい欠陥をもたらすことがある(「PRE31-C. 安全でないマクロの引数では副作用を避ける」を参照)。関数形式マクロでは、各引数を 1 回だけ評価すべきである。可能であれば、関数形式マクロを定義することを避け、インライン関数を使用すべきである(「PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う」を参照)。
違反コード (引数の複数回評価)
安全でない関数形式マクロの最も重大な問題は、以下の違反コード例に示すようなマクロ引数の副作用である。
#define ABS(x) (((x) < 0) ? -(x) : (x)) void f(int n) { int m; m = ABS(++n); /* ... */ }
この違反コード例において、ABS()
マクロの呼び出しは次のコードに展開され、n
は 1 回でなく 2 回インクリメントされる。この挙動は、マクロの内容を知らない人やそもそもマクロを使用している意識のない人を驚かせるかもしれない。
m = (((++n) < 0) ? -(++n) : (++n));
適合コード (インライン関数)
適合コードとしては、同等のセマンティクスを持つインライン関数を定義することを推奨する。
inline int Abs(int x) { return x < 0 ? -x : x; }
適合コード (言語拡張)
処理系によっては、前述の ABS()
マクロを安全な関数形式マクロとして定義できるような言語拡張機能が用意されている(この言語拡張機能がなければ、引数を 2 回以上評価しなければならなくなる)。たとえば、GCC 拡張の「式中の文と宣言」を使用すると、マクロ ABS()
を安全に実装することができる。ただし、処理系定義の言語拡張機能を使用すると、望ましくないプラットフォームの依存性が発生しコードの可搬性が低下するため、可搬性の高いコードを重視する観点からは、このような拡張機能の使用は避けるべきである(「MSC14-C. 必要もなくプラットフォーム依存のコードを書かない」を参照)。
#define ABS(x) ({__typeof (x) tmp = (x); tmp < 0 ? -tmp : tmp; })
リスク評価
副作用のある引数で安全でないマクロ呼出しを行うと、副作用が何度も発生することになる。これにより、予期しない動作や未定義の動作につながる可能性がある。
ルール |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
PRE12-C |
低 |
中 |
低 |
P6 |
L2 |
自動検出(最新の情報はこちら)
ツール | バージョン | チェッカー | 説明 |
---|---|---|---|
ECLAIR |
1.2 |
CC2.PRE12 |
実装済み |
関連するガイドライン
CERT C++ Secure Coding Standard | PRE12-CPP. Do not define unsafe macros |
翻訳元
これは以下のページを翻訳したものです。
PRE12-C. Do not define unsafe macros (revision 31)