warning: macro expands to multiple statements
警告:マクロは複文に展開される
[-Wmultistatement-macros]
■1.危険な複文マクロ
extern void foo() ;
extern void bar() ;
extern void baz() ;
#define MACRO_3line() foo();bar();baz();
void f1(int x)
{
if(x)
MACRO_3line() ;
}
MACRO_3line()のマクロ定義部分を波括弧で囲っていないので、
マクロを展開するとbar()とbaz()がif文のthen節からはみ出ます。
■2.トリッキーなdo-while(0)マクロ
//複文を do-while(0)で囲ったマクロ
#define MACRO_3line() do{foo();bar();baz();}while(0)
void f2(int x)
{
if(x)
MACRO_3line() ;
else {
;
}
}
複数の文からなるマクロを do-while(0) ループで包む事をメジャールールで推薦していますが、トリッキーな技なのでこのサイトでは推奨しません。
※このdo-while(0)で囲むとif文のthen節で波括弧が無くてもコンパイルエラーにならないのがこの技の売りです。
マクロの展開は以下のコマンドで確認できます。
gcc xxx.c -E | indent -kr #xxx.c は展開したいファイル名
■3.括弧で囲ったマクロ
#define MACRO_3line() {foo();bar();baz();}
void f3(int x)
{
if(x) {//小細工しないで波括弧を書けばよい
MACRO_3line() ;
} else {
;
}
}
単純に関数マクロの定義部と参照部を波括弧で囲めば問題は発生しません。
わざわざトリッキーなdo~while(0)構文を使う必要はありません。
しかし、関数マクロは予期せぬ副作用(予期せぬマクロ展開)があるので、関数マクロはそもそも推奨しません。
■4.inline関数の勧め(推奨)
static inline void MACRO_3line(void) {
foo();
bar();
baz();
}
void f4(int x) {
if(x)
MACRO_3line() ;
else {
;
}
}
関数マクロの使用を避けて、inline 関数を使いましょう。
「inline 関数はメジャールールで禁止されている」と言う場合は
最新のメジャールールを確認しましょう。
2012年度版では inline 関数は解禁されています。
参考:
PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う
https://www.jpcert.or.jp/sc-rules/c-pre00-c.html