PRE02-C. マクロ置換リストは括弧で囲む
マクロ置換リストは括弧で囲み、優先度の低い演算子をその周囲の式から保護すべきである。「PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う」および「PRE01-C. マクロ内の引数名は括弧で囲む」も参照のこと。
違反コード
以下に示す CUBE()
マクロ定義は置換リストを括弧で囲んでいない。
#define CUBE(X) (X) * (X) * (X)
int i = 3;
int a = 81 / CUBE(i);
そのため、次のような呼び出し
int a = 81 / CUBE(i);
は以下のように展開される。
int a = 81 / i * i * i;
これは以下のように評価される。
int a = ((81 / i) * i) * i); /* 243 に評価される */
これは意図した動作ではない。
適合コード
置換リストを括弧で囲むことで、CUBE()
マクロはこの手の呼び出しに対して正しく展開される。
#define CUBE(X) ((X) * (X) * (X))
int i = 3;
int a = 81 / CUBE(i);
ただし、この適合コードは「PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う」に違反している。インライン関数として実装する方がよい。
違反コード
以下の違反コード例では、END_OF_FILE
を -1
として定義している。マクロ置換リストは、単項否定演算子の後に整数リテラル 1
が続いている。
#define END_OF_FILE -1
/* ... */
if (getchar() END_OF_FILE) {
/* ... */
}
このコード例では、条件文を getchar() != END_OF_FILE
と書くべきところ、うっかり比較演算子を書き忘れてしまっている。(「void MSC02-C. Avoid errors of omission」を参照。) マクロが展開されると、条件式となるべき部分が演算式 getchar()-1
として評価されてしまう。これは構文的には正しいのだが、もちろんプログラマの意図したところではない。この例はまた、「DCL00-C. 不変(immutable)オブジェクトは const 修飾する」にも違反している。
END_OF_FILE
の定義で -1
を括弧で囲めば、マクロ展開は正しく評価される。
#define END_OF_FILE (-1)
一度この変更を行えば、この違反コード例はコンパイルできなくなる。マクロが展開されると条件式は getchar() (-1)
になるが、これはもはや構文的には正しくないからだ。END_OF_FILE
のあとには空白が必要なことに注意。空白がないと関数形式マクロになってしまう(−1
は仮引数にはなりえないのでマクロ定義として正しい形式になっていない)。
適合コード
以下の適合コードでは、「DCL00-C. 不変(immutable)オブジェクトは const 修飾する」に適合するよう、マクロ定義を列挙定数に置き換えている。また、EOF
は <stdio.h>
ヘッダで定義された予約済みマクロであり、「DCL37-C. 予約済みの識別子を宣言または定義しない」に適合するために別の識別子を使用する必要がある。
enum { END_OF_FILE = -1 };
/* ... */
if (getchar() != END_OF_FILE) {
/* ... */
}
例外
PRE02-C-EX1: 単一の識別子もしくは関数呼び出しに展開されるマクロは、周囲の式の中の演算子の優先順位の影響を受けない。したがって、そのようなマクロの置換リストには括弧をつけなくてもよい。
#define MY_PID getpid()
PRE02-C-EX2: 配列の添字演算子 []
を使った配列参照に展開されるマクロや、.
演算子や ->
演算子を使って構造体のメンバや共用体オブジェクトを意味する式に展開されるマクロの場合は、演算子が前についても評価順序に影響はないので、括弧を付けなくてもよい。
#define NEXT_FREE block->next_free
#define CID customer_record.account.cid
#define TOOFAR array[MAX_ARRAY_SIZE]
リスク評価
マクロ置換リストを括弧で囲まないと予期せぬ結果を引き起こすおそれがある。
レコメンデーション |
深刻度 |
可能性 |
修正コスト |
優先度 |
レベル |
---|---|---|---|---|---|
PRE02-C |
中 |
中 |
低 |
P12 |
L1 |
自動検出
ツール | バージョン | チェッカー | 説明 |
---|---|---|---|
CodeSonar | 4.4 |
LANG.PREPROC.MACROEND LANG.PREPROC.MACROSTART |
Macro Does Not End With ) or } Macro Does Not Start With ( or { |
1.2 |
CC2.PRE02 |
実装済み | |
Klocwork | 2017 | MISRA.DEFINE.BADEXP | |
LDRA tool suite | 9.5.6 |
77 S |
実装済み |
Parasoft C/C++test | 9.5 | MISRA-096 | Fully implemented |
PRQA QA-C | 8.2 | 3409 | 実装済み |
関連するガイドライン
SEI CERT C++ Coding Standard | PRE02-CPP. Macro replacement lists should be parenthesized |
ISO/IEC TR 24772:2013 | Operator Precedence/Order of Evaluation [JCW] Pre-processor Directives [NMP] |
参考資料
[Plum 1985] | Rule 1-1 |
[Summit 2005] | Question 10.1 |
翻訳元
これは以下のページを翻訳したものです。
PRE02-C. Macro replacement lists should be parenthesized (revision 150)