【C言語】do-while(0) の意味|なぜループしない書き方が使われるのか

技術

C言語のソースコードを読んでいると、
次のように、複数の処理が do-while(0) で囲まれたソースコードを見かけることがあります。

#inlude <stdio.h>

#define OK 0
#define NG -1

int main( void ){
    int ret = NG;
    do{
        if( OK != funcA() ){
            break;
        }
        if( OK != funcB() ){
            break;
        }
        if( OK != funcC() ){
            break;
        }
        ret = OK;
    }while( 0 );

    return ret;
}

do-while 文は、本来「設定した条件が真の間、処理を繰り返す」ための制御構文です。
しかし、do-while(0) では、条件式が常に偽となるため、ループが繰り返されることはありません。

一見すると、意味のない制御構文のようにも見えますが、
この書き方にも意図された狙いがあります。

この記事では、 do-while(0) が使われる理由について、
備忘録も兼ねて整理してまとめます。

do-while(0)とは

do-while 文の基本動作

do-while 文は、while 文とは異なり、
処理を実行したあとに条件式を評価するループ構文です。

基本的な構文は次のとおりです。

do {
    処理;
} while (条件式);

この構文では、まず do { … } 内の処理が実行され、その後に条件式を評価します。
条件式が真(true)の場合は再び処理が繰り返され、偽(false)の場合はループを抜けます。

そのため、do-while 文は、
最低でも1回は処理を実行したい」場合に適した制御構文です。

do-while(0) の特徴

do-while(0) には、次のような特徴があります。

  • 必ず1回だけ処理が実行される
  • break文を使うことで、処理の途中でブロック全体を抜けられる
  • 処理群をひとまとまりのブロックとして扱える

この性質を利用することで、複数の処理を順番に実行しつつ、
途中でエラーが発生した場合は、その時点で処理を中断することができます。

たとえば、冒頭のソースコードにおける funcB() の実行条件が
「事前に funcA() の処理が正常終了していること」だったとします。

このような場合、funcA() ~ funcC() を do-while(0) で囲んでおくことにより、
funcA() で異常が発生した時点で break 文により処理を中断されるため、
funcB() 以降の処理は実行されなくなります。

do-while(0)で囲む理由

do-while(0) を使ったソースコードを見て、
goto文途中returnで中断したらよいのでは?」と感じる人も多いでしょう。
筆者自身も、その考え方自体は正しいと思います。

しかし、実際の開発では現場ごとにコーディング規約と呼ばれる独自のルールが定められており、
必ずしもそれらの構文が自由に使えるとは限りません。

break 文を使って処理を中断するため

開発現場によっては、次のようなコーディング規約が存在することがあります。

  • goto文の使用が許されていない
  • return文は1関数につき1つまでと決められている。
    (=関数の終端以外での return を禁止)

このような制約がある場合、
処理の途中で関数を抜ける手段が限られてしまいます。

do-while(0) で処理を囲んでおくことによって、
エラー発生時に break 文を実行することで、
関数の末尾まで到達することなく処理を中断できます。

このように、「goto や途中return が使えない環境で処理を中断する手段」として、
do-while(0) が用いられることがあります。
苦肉の策とも……。

処理を1つのブロックとしてまとめるため

do-while(0) で複数の処理を囲むことで、
一連の処理を「ひとつのまとまり」として扱うことができます。

これにより、if 文を多重にネストする必要がなくなるため、
エラー処理の流れを上から順に追いやすくなる点もメリットになります。

この書き方は、特に「前の処理が成功した場合のみ次へ進む」という処理が
連続する場面で有効です。

まとめ

do-while(0) は、複数の処理をひとつのブロックとしてまとめ、
途中で処理を中断するために使われます。

特に goto 文や途中 return が使用できないコーディング規約下において、
break 文による処理の中断を実現する手段として有効です。

コメント