1. ホーム
  2. c++

[解決済み] マクロで無意味なdo-while文やif-else文を使っているのはなぜですか?

2022-03-22 21:54:03

質問

多くのC/C++マクロで、マクロのコードが無意味な do while のループになります。 以下はその例です。

#define FOO(X) do { f(X); g(X); } while (0)
#define FOO(X) if (1) { f(X); g(X); } else

が何なのかがわからない。 do while は何をしているのでしょうか? なぜ、それをしないで書かないのですか?

#define FOO(X) f(X); g(X)

解決方法は?

その do ... whileif ... else があるのは マクロの後のセミコロンは、常に同じ意味を持ちます。 例えば のように、2つ目のマクロがありました。

#define BAR(X) f(x); g(x)

ここで、もしあなたが BAR(X); の中に if ... else 文の本文が中括弧で囲まれていない場合、あなたは悪い驚きを感じるでしょう。

if (corge)
  BAR(corge);
else
  gralt();

上記のコードは、次のように展開されます。

if (corge)
  f(corge); g(corge);
else
  gralt();

というのは、elseがもはやifに関連付けられていないため、構文的に正しくありません。 マクロの中で中括弧で囲んでも、中括弧の後のセミコロンは構文的に正しくないので、役に立ちません。

if (corge)
  {f(corge); g(corge);};
else
  gralt();

この問題を解決するには、2つの方法があります。 1 つ目は、カンマを使用してマクロ内のステートメントを連続させ、式のように動作する能力を失わせない方法です。

#define BAR(X) f(X), g(X)

上記のバージョンのバー BAR は、上記のコードを次のように展開し、構文的に正しいものにします。

if (corge)
  f(corge), g(corge);
else
  gralt();

の代わりに f(X) のように、独自のブロックに入れる必要がある複雑なコードがある場合、例えばローカル変数を宣言する場合などです。 最も一般的なケースでは、次のようなものを使用します。 do ... while を使用すると、マクロは混乱することなくセミコロンを取る単一のステートメントになります。

#define BAR(X) do { \
  int i = f(X); \
  if (i > 4) g(i); \
} while (0)

を使用する必要はありません。 do ... while で何か作ることができます。 if ... else を使用した場合にも、同様に if ... else の中で展開されます。 if ... else につながり、" ダングリング else "のように、既存のdangling elseの問題をさらに見つけにくくする可能性があります。

if (corge)
  if (1) { f(corge); g(corge); } else;
else
  gralt();

ポイントは、ダングリングセミコロンが誤りである文脈で、セミコロンを使い切ることです。 もちろん、この時点では、セミコロンを使わずに BAR をマクロではなく、実際の関数として使用します。

まとめると do ... while は、C言語プリプロセッサの欠点を回避するために存在します。 C言語のスタイルガイドで、C言語のプリプロセッサは使わないようにと書かれているのは、このようなことを心配してのことなのです。