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

PRE10-C. 複数の文からなるマクロは do-while ループで包む

複数の文を一つのグループとして実行するために、マクロがよく使われる。

この手の仕事には、一般にインライン関数の方が適している(「PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う」を参照)。しかし、インライン関数を使えない場面もある(たとえば、異なる型の変数に対する演算を行うときなど)。

マクロの中に複数の文が含まれている場合、これらの文は do-while ループで構文的にひとまとめにしておくべきである。そうすることで、if 節の中など、単一の文や文のブロックが期待される箇所において、安全にマクロを使うことができる。あるいは、if文、for文、while文において、本体が単一の文であっても波括弧を使用することで、マクロ中の複数の文は do-while ループを使わなくても正しく展開される。「EXP19-C. 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;
違反コード

以下の違反コード例では、複数の文を正しくひとまとめにしていない。

/*
 * 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ループは常に一度だけ実行される。

リスク評価

マクロの本体を適切に do-while ループで包んでおかないと、予期せぬあるいは原因究明が困難な動作を引き起こすおそれがある。

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

PRE10-C

P12

L1

自動検出
ツール バージョン チェッカー 説明
PRQA QA-C 8.1

3412
3458

実装済み
関連するガイドライン
CERT C++ Secure Coding Standard PRE10-CPP. Wrap multistatement macros in a do-while loop
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 78)

Top へ

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