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

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

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;

さらに、このマクロは「PRE02-C. マクロ置換リストは括弧で囲む」にも違反している.

違反コード

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

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

このコード例でも、マクロ引数が2回評価されるため、「PRE12-C. 安全でないマクロを定義しない」に違反している。実引数には単純な左辺値が渡されることが想定されている。

リスク評価

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

レコメンデーション

深刻度

可能性

修正コスト

優先度

レベル

PRE10-C

P12

L1

自動検出(最新の情報はこちら
ツール バージョン チェッカー 説明
Axivion Bauhaus Suite

6.9.0

CertC-PRE10
Klocwork
2018
MISRA.DEFINE.BADEXP
LDRA tool suite
9.7.1
79 S Enhanced enforcement
Polyspace Bug Finder

R2020a

CERT C: Rec. PRE10-C Checks for macros with multiple statements (rule fully covered)
PRQA QA-C
9.7

3412, 3458

実装済み
関連するガイドライン
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 104)

Top へ

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