Home > ラーニング > セキュアコーディング > C セキュアコーディングスタンダード > 01. プリプロセッサ (PRE)
マクロ定義におけるすべての引数名は括弧で囲むこと。「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-EX1: 置き換えられるテキスト中で引数名がコンマで囲まれている場合、実引数がいかに複雑であっても、マクロの引数に括弧をいれる必要はない。コンマは他のあらゆる演算子よりも優先順位が低いため、実引数が予期せぬ方法で解析されることはない。関数呼び出し時に引数を区切るコンマ区切りは、他の演算子よりも優先順位は低いが、厳密にはコンマ演算子とはことなるものである。
#define FOO(a, b, c) bar(a, b, c) /* ... */ FOO(arg1, arg2, arg3);
PRE01-EX2: マクロ引数を個別に括弧で囲むことができない場合として、## 演算子を用いて字句(token)を連結する場合、# 演算子を用いてマクロ引数を文字列に変換する場合、隣り合う文字列リテラルを連結する場合などがある。以下に示す JOIN() マクロは二つの引数を連結して新しい字句を生成する。SHOW()マクロはひとつの引数を文字列リテラルに変換し、その文字列は次に、隣接する文字列と連結され、printf() 呼び出し時の書式指定を形成する。
#define JOIN(a, b) (a ## b) #define SHOW(a) printf(#a " = %d\n", a)
## 演算子を用いた字句の連結に関する詳細は、「PRE05-C. 字句の結合や文字列化を行う際にはマクロ置換をよくよく理解して行う」を参照のこと。
マクロ中の引数名を括弧で囲まないと、予期せぬプログラムの動作を引き起こす可能性がある。
| レコメンデーション | 深刻度 | 可能性 | 修正コスト | 優先度 | レベル |
|---|---|---|---|---|---|
| PRE01-C | 中 | 中 | 低 | P12 | L1 |
LDRA tool suite V 7.6.0 はこのレコメンデーションの違反を検出することができる。
PRE01-C. Use parentheses within macros around parameter names