JPCERT コーディネーションセンター

PRE01-C. マクロ内の引数名は括弧で囲む

PRE01-C. マクロ内の引数名は括弧で囲む

マクロ定義におけるすべての引数名は括弧で囲むこと。「PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う」と「PRE02-C. マクロ置換リストは括弧で囲む」も参照。

違反コード

以下の CUBE() マクロ定義は、引数名を括弧で囲んでいない。

#define CUBE(I) (I * I * I)

そのため、次のような呼び出し

int a = 81 / CUBE(2 + 1);

は次のように展開される。

int a = 81 / (2 + 1 * 2 + 1 * 2 + 1);  /* 11 に評価される */

これは明らかに、求めようとした結果ではない。

適合コード

CUBE() マクロのすべての引数名を括弧で囲むことで、次のように呼び出されるときに正しく展開されるようになる。

#define CUBE(I) ( (I) * (I) * (I) )
int a = 81 / CUBE(2 + 1);
例外

PRE01-C-EX1: 置き換えられるテキスト中で引数名がコンマで囲まれている場合、実引数がいかに複雑であっても、マクロの引数に括弧をいれる必要はない。コンマは他のあらゆる演算子よりも優先順位が低いため、実引数が予期せぬ方法で解析されることはない。関数呼び出し時に引数を区切るコンマ区切りは、他の演算子よりも優先順位は低いが、厳密にはコンマ演算子とは異なるものである。

#define FOO(a, b, c) bar(a, b, c)
/* ... */
FOO(arg1, arg2, arg3);

PRE01-C-EX2: マクロ引数を個別に括弧で囲むことができない場合として、## 演算子を用いて字句(token)を連結する場合、# 演算子を用いてマクロ引数を文字列に変換する場合、隣り合う文字列リテラルを連結する場合などがある。以下に示す JOIN() マクロは 2 つの引数を連結して新しい字句を生成する。SHOW() マクロでは、その引数を文字列リテラルに変換し、さらに隣接する文字列と連結した形で printf() の第1引数とし、同時に %d に対応する printf() の第2引数としても展開している。たとえば、SHOW(66); というコードは printf("66" " = %d\n", 66); に展開される。

#define JOIN(a, b) (a ## b)
#define SHOW(a) printf(#a " = %d\n", a)

## 演算子を用いた字句の連結に関する詳細は、「PRE05-C. 字句の結合や文字列化を行う際のマクロ置換動作をよく理解する」を参照のこと。

リスク評価

マクロ中の引数名を括弧で囲まないと、予期せぬプログラムの動作を引き起こす可能性がある。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

PRE01-C

P12

L1

自動検出(最新の情報はこちら
ツール バージョン チェッカー 説明
ECLAIR 1.2 CC2.PRE01 実装済み

LDRA tool suite

9.5.6

78 S

Enhanced Enforcement

Parasoft C/C++test 9.5 MISRA2004-19_10 Fully implemented
PRQA QA-C 8.2 3410 実装済み
関連するガイドライン
SEI CERT C++ Coding Standard PRE01-CPP. Use parentheses within macros around parameter names
ISO/IEC TR 24772:2013 Operator Precedence/Order of Evaluation [JCW]
Pre-processor Directives [NMP]
MISRA C:2012

Rule 20.7 (required)

参考資料
[Plum 1985]  
[Summit 2005] Question 10.1
翻訳元

これは以下のページを翻訳したものです。

PRE01-C. Use parentheses within macros around parameter names (revision 149)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter