繰返し文と分岐文

こんにちは、めのんです!

前回は選択文を解説したんですけど、本来ならその際にいっしょにやらないといけなかった分岐文やラベル付き文の解説が抜けていました。
これらは今回の主題である繰返し文とも関連性が深いので、今回あわせて解説することにします。

繰返し文

Cの繰返し文には「do」「while」「for」の3つがあります。
PHPにはあったforeachはCにはありませんのでご注意ください。

では、それぞれの繰返し文を順に解説しています。

do文

おそらく繰返し文の中で一番マイナーなのはdo文ではないかと思います。
それはPHPでもきっと同じですよね。

でも、do文は繰返し文の中では一番基本的なものなんです。
なぜなら条件分岐ひとつで実現してしまうからです。
たとえば、

do
  文
while (条件);

というdo文は、次のコードと同じです。

_loop;
  文
if (条件) goto _loop;

このサンプルはPHPのコードですが、Cでも同じで、最後のif文は機械語では1命令になることが多いんです(条件を評価するための機械語は別に必要です)。

PHPのdo文(PHPではdo-while文ですね)とCのdo文はほとんどいっしょなので、同じ感覚で使うことができます。

違いといえば、

  • 制御構造に関する別の構文が使えない

だけです。

while文

while文はdo文に次いで簡単です。
do文と同じようにPHPのコードから見ていきましょう。

たとえば、

while (条件)
  文

というwhile文は、次のコードと同じです。

goto _continue;
_loop;
  文
_continue:
  if (条件) goto _loop;

Cのwhile文もPHPのwhile文とほとんど同じです。

違いはやはり

  • 制御構造に関する別の構文が使えない

だけです。

for文

for文はさきほどの2つに比べれば複雑ですが、PHPプログラマーのみなさんにはおなじみですよね。
さきほどの2つの繰返し文と同じようにPHPのコードを使った例を示します。

for (式1; 式2; 式3)
  文

というfor文は、次のコードと同じです。

式1;
goto _continue;
_loop;
  文
_continue:
  式3;
if (式2) goto _loop;

式1~3は省略可能なことや省略した場合の振る舞いもCとPHPでは同じですのでえ簡単ですね。

ただ、CとPHPのfor文では違うところ(というか説明が必要な点)も少しあります。

  • 制御構造に関する別の構文が使えない
  • 式1では宣言も可能

上にも書いたように、for文の式1には宣言を書くこともできます。
ただし、式1に書くことができる宣言には制限があって、自動記憶域期間を持つオブジェクトだけです。
static指定子を付けた静的記憶域間を持つオブジェクトや関数の宣言などはできません。

分岐文

Cの分岐分には「goto文」「return文」「break文」「continue文」があります。
これもPHPと同じですね。

goto文とラベル付き文

まずはgoto文からですが、「ラベル付き文」も合わせて説明する必要があります。

Cのgoto文はPHPのgoto文とほぼ同じですが、次の点が異なります。

  • 関数の中でしか使えない

関数の中でしか使えないのは選択文や繰返し文も同じなのですが、あえて挙げたのは、goto文によって分岐できる範囲に関わってくるからです。

gotoの分岐先のラベルは関数の中にしか書けませんので、結果的にgoto文で分岐できるのは関数の中だけということになります。

PHPのgoto文は、関数の中で書けば関数の中にしか分岐できませんが、関数の外で書けば結構いろんなところまで飛んでいけます。
たとえば、異なる名前空間にあるラベルにでも飛んで行けてしまいます。
Cのgoto文はそれに比べればずっとおとなしい機能です。

goto文の分岐先に使うラベルですが、PHPではラベル単独でも書くことができたのですが、Cではラベル単独で書くとコンパイルエラーになってしまいます。
Cのラベルはラベル付き文という一種の文の一部ですので、ラベルのあとには必ず文がなければなりません。

  ...
label:  // ラベル単独では存在できない
}

  ...
label:
  ;     // 必ずラベルのあとには文が必要
}

ラベルのあとにラベル付き文を書くこともできます。
switch文の中のcaseも一種のラベルですので、複数のcaseを並べたことがあると思います。
そういうのはラベルのあとにラベル付き文を書いたことになります。

Cでは、ラベルは関数有効範囲を持ちます。
つまり、ブロックの中に書いたラベルであっても、関数全体からそのラベルを参照することができます。

また、ラベル名は専用の名前空間があります。
ですので、関数名やオブジェクト名、構造体のタグなどと同じ名前のラベルを付けても問題ありません(紛らわしいのでできるだけ避けた方がいいとは思います)。

return文

Cのreturn文はPHPもreturn文とほぼ同じですが、関数から呼び出し元に戻るときだけ使うことができます。
PHPではincludeやrequireなどで取り込まれたスクリプトから戻るときもreturn文を使えますが、Cにはそのような仕様はありません。

あと、以前にも書きましたが、配列を値で返すことはできません。
配列の先頭要素へのポインタであれば返すことはできます。
ただし、自動記憶域期間を持つ配列をポインタで返した場合、関数から抜けるとその配列はすでに失われていますので注意が必要です。

構造体であれば問題なく値で返すことができます。
ただし、大きな配列を値返しすると効率が悪いですし、メモリ不足につながる可能性もあるので、無制限というわけにはいきません。
Cでは構造体も値よりポインタで関数に渡してポインタで返すことの方が多いようです。

break文

break文はswitch文や繰返し文から脱出するときに使います。
使い方はPHPと同じと思っていただいてかまいません。

念のため、for文からbreak文で脱出するときの動作をPHPのコードを使って確認しておきますね。

for (式1; 式2; 式3)
{
  ...
  break;
  ...
}

というbreak文は、次のコードと同じです。

式1;
goto _continue;
_loop;
  ...
  goto _break;  // break文に相当
  ...
_continue:
  式3;
if (式2) goto _loop;
_break:

continue文

最後はcontinue文です。
これもPHPと同じなんですが、使う機会が少ないこともあって正しく理解できていない方もいらっしゃると思います。

念のためPHPのコードを使ってcontinue文の振る舞いをおさらいしておきますね。

for (式1; 式2; 式3)
{
  ...
  continue;
  ...
}

というcontinue文は、次のコードと同じです。

式1;
goto _continue;
_loop;
  ...
  goto _continue;  // continue文に相当
  ...
_continue:
  式3;
if (式2) goto _loop;
_break:

以上、駆け足でしたが繰返し文と分岐文(とラベル付き文)について解説しました。
いかがだったでしょうか?
PHPで使い慣れている文だとは思いますが、たまにはこうやっておさらいしてみるのも悪くないと思います。

それでは次回はいよいよ割付け記憶域期間の解説を行いたいと思います。
もしかすると1回では収まらなくて何回かに分けるかもしれません。
どうぞご期待ください!