【C言語】
do~while(0)関数マクロは
避ける
~inline関数の勧め~

warning: macro expands to multiple statements

警告:マクロは複文に展開される
[-Wmultistatement-macros]

この記事の概要

この記事は、C言語において関数マクロ使用のリスクと代替としてのinline関数の活用を解説しています。

do-while(0)マクロの利用を避け、複文展開の際に警告が出る場合の対処法も含まれ、安全でメンテナンスしやすいコード作成のためのアプローチを紹介しています。


■危険な複文マクロ

#include <stdio.h>
void    foo() {puts("foo");}
void    bar() {puts("bar");}
void    baz() {puts("baz");}

#define MACRO_3Line()   foo();bar();baz();

int main(int argc,char *argv[]){
    if(argc == 3)
        MACRO_3Line() ;
}

MACRO_3line()のマクロ定義部分を波括弧で囲っていないので、
マクロを展開するとbar()とbaz()がif文のthen節からはみ出ます。


■複文マクロを波括弧で囲む

#include <stdio.h>
void    foo() {puts("foo");}
void    bar() {puts("bar");}
void    baz() {puts("baz");}

#define MACRO_3Line()   {foo();bar();baz();}

int main(int argc,char *argv[]){
    if(argc == 3)
        MACRO_3Line() ;
    else {
        ;
    }
}

単純に関数マクロの定義部と参照部を波括弧で囲めば問題は発生しませんが

このコードのように、
マクロ呼び出しのif文then節に波括弧がないと
コンパイルエラーになってしまします。

以下のコマンドでマクロ展開すると
コンパイルエラーになる理由がわかります。
gcc xxx.c -E | indent -kr  
※xxx.c は展開したいファイル名


■do-while(0)関数マクロはわかりにくいので避ける

#include <stdio.h>
void    foo() {puts("foo");}
void    bar() {puts("bar");}
void    baz() {puts("baz");}

#define MACRO_3Line()   do{foo();bar();baz();}while(0)

int main(int argc,char *argv[]){
    if(argc == 3)
        MACRO_3Line() ;
    else {
        ;
    }
}

複数の文からなるマクロを
do-while(0) ループで包む事を
メジャールールで推薦していますが、

回らないループのdo-while(0)は
不自然で難解なので
筆者は推奨しません。


■do-while(0)関数マクロよりinline関数がお勧め

#include <stdio.h>
void    foo() {puts("foo");}
void    bar() {puts("bar");}
void    baz() {puts("baz");}

static inline void MACRO_3Line(void){
    foo();
    bar();
    baz();
}

int main(int argc,char *argv[]){
    if(argc == 3)
        MACRO_3Line() ;
    else {
        ;
    }
}

関数マクロは予期せぬ副作用(予期せぬマクロ展開)があるので、
関数マクロはそもそも推奨しません。
関数マクロを避けて、
inline 関数を使いましょう。


inline関数はMISRA-Cルールで禁止されている?

と言う場合は
最新のルールを確認しましょう。
2012年度版では inline 関数は解禁されています。

参考:

PRE00-C. 関数形式マクロよりもインライン関数やスタティック関数を使う

https://www.jpcert.or.jp/sc-rules/c-pre00-c.html