web-dev-qa-db-ja.com

{...} while(0)—何に役立つのですか?

可能性のある複製:
C/C++マクロにdo/whileおよびif/elseステートメントが無意味になることがあるのはなぜですか?

私は今、10年以上その表現を見てきました。私はそれが何のために良いのか考えようとしてきた。私は主に#definesでそれを見るので、それは内部スコープ変数宣言とブレーク(gotoの代わりに)を使用するのに良いと思います。

それは他に何か良いですか?使いますか?

299
gilm

これは、マルチステートメント操作を#defineに使用し、その後にセミコロンを入れて、ifステートメント内で使用できるCの唯一の構成体です。例が役立ちます:

#define FOO(x) foo(x); bar(x)

if (condition)
    FOO(x);
else // syntax error here
    ...;

中括弧を使用しても役に立たない:

#define FOO(x) { foo(x); bar(x); }

ifステートメントでこれを使用するには、直感に反するセミコロンを省略する必要があります。

if (condition)
    FOO(x)
else
    ...

FOOを次のように定義する場合:

#define FOO(x) do { foo(x); bar(x); } while (0)

次の構文は正しいです:

if (condition)
    FOO(x);
else
    ....
447
Greg Hewgill

これはエラーチェックを簡素化し、深いネストされたifを回避する方法です。例えば:

do {
  // do something
  if (error) {
    break;
  }
  // do something else
  if (error) {
    break;
  }
  // etc..
} while (0);
101
Jere.Jones

関数のようなマクロを実際に関数として使用できるように、複数のステートメントを単一のステートメントにグループ化するのに役立ちます。あなたが持っていると仮定します

#define FOO(n)   foo(n);bar(n)

そしてあなたはする

void foobar(int n){
  if (n)
     FOO(n);
}

その後、これは

void foobar(int n){
  if (n)
     foo(n);bar(n);
}

2番目の呼び出し(bar(n))はifステートメントの一部ではなくなったことに注意してください。

両方をdo {} while(0)にラップし、ifステートメントでマクロを使用することもできます。

69

Do {} while(0)ループが機能しない次の状況に注意するのは興味深いことです。

値を返す関数のようなマクロが必要な場合は、do {} while(0)の代わりに statement expression :({stmt; stmt;})が必要です。


#include <stdio.h>

#define log_to_string1(str, fmt, arg...) \
    do { \
        sprintf(str, "%s: " fmt, "myprog", ##arg); \
    } while (0)

#define log_to_string2(str, fmt, arg...) \
    ({ \
        sprintf(str, "%s: " fmt, "myprog", ##arg); \
    })

int main() {
        char buf[1000];
        int n = 0;

        log_to_string1(buf, "%s\n", "No assignment, OK");

        n += log_to_string1(buf + n, "%s\n", "NOT OK: gcc: error: expected expression before 'do'");

        n += log_to_string2(buf + n, "%s\n", "This fixes it");
        n += log_to_string2(buf + n, "%s\n", "Assignment worked!");
        printf("%s", buf);
        return 0;
}
17
ubuntu-fanboy