第3章 プログラムの流れの分岐

テキスト『新・明解C言語 入門編』第3章の内容に関する補足説明を提供します.

3-1 if 文

if 文・その1

List 3-1 のプログラムは, 入力した数値が5で割り切れるときには何も表示しません. この点を改良したプログラムは List 3-3 です.

奇数の判定

List 3-2 のプログラムは, 入力した数値が偶数のときには何も表示しません. この点を改良したプログラムは List 3-4 です.

if 文・その2

このセクションについての補足説明は現在のところありません.

偶数と奇数の判定

このセクションについての補足説明は現在のところありません.

非ゼロの判定

このセクションについての補足説明は現在のところありません.

if 文の構文図

このセクションについての補足説明は現在のところありません.

等価演算子

このセクションについての補足説明は現在のところありません.

剰余の判定

演算子には優先順位があります. 演算子の一覧と優先順位はテキストの p.205 を参照してください. わかりやすいのは2項の四則演算を行う演算子(+, -, *, /)ですね. たとえば,a + b * c という式では, 2項 * 演算子の優先順位(4)が2項 + 演算子の優先順位(5)よりも高いため, b * c が最初に計算され,次にその計算結果が a に加えられます.

List 3-8 の if 文での制御式(control expression)


(num % 10) == 5

は,プログラムの下の注釈に書かれているように, ( ) を省略して


num % 10 == 5

としてもかまいません. % 演算子の優先順位(4)は == 演算子の優先順位(8)よりも高いので, ( ) がなくても num % 10 が先に実行されます. しかし,( ) をつけておいた方が読みやすいと思います.

関係演算子

このセクションについての補足説明は現在のところありません. List 3-9 での


else if (no > 0)

という記述について,次のセクションで説明があります.

入れ子になった if 文

List 3-9 での条件分岐では,else if という構文があるように見えます. すなわち,


if ( 条件式 1 )
   文 1
else if ( 条件式 2 )
   文 2
else if ( 条件式 3 )
   文 3

.....

else
   文 N

というように,最初の条件式は if の次に書き, 2番目以降の条件式は else if で並べるように見えます. 条件分岐の書き方としてはこれで正しい(かつ,わかりやすい)のですが, テキスト p.51 で説明されているように, else if という特別な構文はありません. if 文が入れ子になっているだけです.

テキスト p.58 で説明されている 複合文(compound statement)を用いて, List 3-9 での if 文の入れ子構造を明示してみます. 入れ子にされた if 文全体を { } で囲みます.

Nested if else

インデント(テキスト p.105 参照)をやり直します. コントロール [Ctrl] キーを押しながら, [c] キーを押してください. 続いて,コントロール [Ctrl] キーを押しながら, [q] キーを押してください. これで適切なインデントがされます. この操作を C-c C-q と書きます. プログラムを修正してインデントが崩れたときには, こうしてインデントをやり直すとよいでしょう.

Nested if else

入れ子の外側の if に続く文も { } で囲むと, 下図のようになります. テキスト p.59 で説明されているように, if 文が制御する文は1個だけ(if の後に1個, else の後に1個)なので, 常にこうして { } で文を囲むスタイルを採用してもよいでしょう(テキスト p.59).

List 3-9

演習 3-3 では,入力された整数の符号で条件を分岐して,


if (no >= 0)
   printf("絶対値は%dです.\n", no);
else
   printf("絶対値は%dです.\n", -no);

という if 文を書くことをすぐに思いつくでしょう. 符号を変えるために単項 - 演算子を使います. もし,"絶対値は%dです.\n" と,2回同じことを書くのが見苦しいと感じるなら, これを1回だけ書くプログラムにすることも可能です(else を使わない). 考えてみてください. List 3-11 と List 3-12 を比較すると,ヒントが得られるでしょう.

評価

演習 3-5 では,等価演算子関係演算子を用いた式を書き, その式を評価した結果を printf 関数で表示してやります. たとえば,次のようなプログラムを書くことができます.

演習 3-5

このプログラムを実行すると,評価値が次のように表示されます.

演習 3-5 の実行

大きいほうの値を求める

このセクションについての補足説明は現在のところありません.

三値の最大値を求める

このセクションについての補足説明は現在のところありません.

条件演算子

List 3-14 での


max = (n1 > n2) ? n1 : n2

という式では,評価の順序に注意してください.

テキスト p.205 の演算子一覧を見ると, 条件演算子(? :)は, 単純代入演算子(=)よりも,優先順位が高くなっています. したがって,最初に


(n1 > n2) ? n1 : n2

が評価され,その結果が変数 max に代入されることになります.

差を求める

演習 3-9 は,どうすればよいのか, アイデアを思いつかないかもしれません. 正解を教えられてしまえば, それを理解することはそんなに難しくないと思いますが. ヒントとして,条件式(conditional expression)の 第2および第3オペランド,すなわち, Fig. 3-9 での式 2 および式 3 は, 式でありさえすれば何でもよいことに注意してください. これらの式で条件演算子を使うことができます.

複合文(ブロック)

複合文(compound statement)の中で変数の宣言を行う場合には, テキスト(p.58)でも説明されているように, ブロックの先頭で行ってください. これまで書いてきたプログラムでも, main 関数のブロックの先頭で変数の宣言を行っていました.

実は,C 言語の最近の規格(C99 および C11)では, ブロックの先頭でなくても変数の宣言が許されるようになりました. しかし,変数の宣言をブロックの先頭にまとめた方が, 宣言忘れなどのミスを発見しやすいと思います. この変更を支持していないプログラマもいるようです(たとえば, 次の文献の p.44 の記述を参照).

前橋和弥 (2001) C言語 体当たり学習徹底入門 技術評論社

論理演算子

演習 3-10 では,判定する条件の順序に注意してください. 問題文に書いてある順序で, 最初に3つの数値がすべて等しいかどうかを判定し, 次にいずれか2つの数値が等しいかどうかを判定するとうまくいきます. 次のようなプログラムを書きます.

演習 3-10

条件判定の順序を変え,


(na == nb || nb == nc || nc == na)

を最初にテストして, この条件が満たされた場合に「二つの値が等しいです」と表示しようとすると, 3つの数値がすべて等しい場合にもこのメッセージが表示されてしまいます.

一般に,条件分岐を行うプログラムにおいて, 条件をテストする順序を考えることはしばしば重要になります.

演習 3-11 では 論理OR演算子(logical OR operator)を使うという指示がありますが, 使わない方が自然なプログラムになります. たとえば,次のように書くことができます. 2数の差を代入する変数 diff の値を表示しているのは確認のためです.

演習 3-11

条件式を評価した結果を変数(diff)に代入することは, List 3-14 で行いました. このプログラムを実行すると,以下のようになります. 負の整数が入力された場合も正しく動作しています.

演習 3-11 の実行

上のプログラムで,単に diff = na - nb; とすると, diff の値が正の場合と負の場合があります. 論理OR演算子を使うなら,


if (diff > 10 || diff < -10)
   puts("それらの差は11以上です。");
else
   puts("それらの差は10以下です。");

と書くことになるでしょう.

Column 3-2 では, 3つの変数の等価性の判定に == を利用するという誤りが取り上げられています. これは確かに誤りなのですが, その理由の説明がおかしいです. if (a == b == c) という書き方がよくないのは, == 演算子が2項演算子だからではありません. この書き方そのものは誤りでなく,エラーにはなりません. これは if ((a == b) == c) と解釈されます. そうすると,意図したようには評価が行われないことに気がつくでしょう. たとえば,a, b, c の値はすべて 2 であるとしましょう. 数学の式で書けば a = b = c ですが, a == b == c を評価した結果は 0(偽)となってしまいます. 最初に a == b が評価され,その結果は 1 ですね. すると次は 1 == c という式が評価され,この結果は 0 となります.

3-2 switch 文

switch 文と break 文

switch 文の制御式の型は整数でなければなりません. 具体的には,第7章で学習する, 整数型,文字型,列挙型のいずれかです.

制御式のあとに続けることができるのは, ひとつの文だけです(Fig, 3-13 参照). そのため,すべての case を必ず { } で囲って, 複合文(p.58)としてください. case それぞれにおいては,複数の文を並べることができます.

switch 文の case ラベルはどの順序で並べてもかまいません. if 文と異なり, 条件が満たされているかどうかを上から順にテストするわけではないからです. すべての case は並列です.

switch 文の case ラベルの値に使うことができるのは, テキストで説明されているように(p.65),整数の定数です. C 言語では,単一の文字を int 型の定数として扱うので(第4章で学習します), 文字をラベルの値に使うこともできます. たとえば,Yes なら y,No なら n をキーボードから入力させ, 入力に応じて処理を分けるならば.


case 'y' :
   puts(" Yes の場合の出力 "); break;
case 'n' :
   puts(" No の場合の出力 "); break;

というように case を書くことができます.

switch 文では, case ラベルの値に指定することができるのが整数の定数だけなので, 比較的単純な条件分岐しかできません. 単一の整数値で分岐できない場合には,if 文を利用することになります. たとえば,a > 10 のように, 変数への入力値が特定の値より大きいかを判定したいのならば, if 文を使うことになります.

複雑な switch 文

どの case にも該当しない場合の分岐先として, default というラベルを使います. switch 文のラベルは並列なので,default をどこに記述しても構いませんが, やはりすべての case の後に記述するべきでしょう.

List 3-19 のプログラムのように,default は省略可能です. しかし, 制御式を評価した値がどの case とも一致しない場合に, 何も行わないことを明示するため,


default : break;

と必ず記述するようにしてもよいでしょう. 以下の文献ではこのスタイルを推奨しています(p.118).

S. Oualline (1998) C実践プログラミング(第3版) オライリー・ジャパン

switch 文の case は単に分岐先を示すラベルとしての機能しかありません. 特定の分岐先に進んだ後は,処理の終わりを明示してやる必要があります. 処理を終えて switch 文を抜けるためには, List 3-19 で説明されているように,break 文を使います.

break 文を忘れても,文法的なエラーにはならないので, しばしば不具合を引き起こしてしまいます. たとえば,List 3-20 での case 2 には break 文がないため, 文字 C に続いて D も出力されています. しかし,おそらくここで出力したかったのは C だけでしょう. switch 文による条件分岐では break 文を忘れないよう, 十分に注意する必要があります.

一方,case が処理の終わりを決めないことを逆手にとって, OR に相当する条件の判定を行うことができます. List 3-20 では,case 6 と case 7 において, いずれも文字 E を出力します. あたかも (sw == 6 || sw == 7) という式を評価しているかのようです.

switch 文と if 文

switch 文は便利ですが, break 文を忘れるというミスをしやすいものです. そのため,switch 文を使うことはできるだけ避けて, if 文を使うべきだという考え方もあります. たとえば, 『日経ソフトウェア』という雑誌の2013年7月号の特集「そのコードは古い」では,

特別な理由(if 文のネストがどうしても深くなる, switch 文の方が格段に読みやすいなど)がない限り, switch 文は避けるべきだ.

と書かれています.

選択文

このセクションについての補足説明は現在のところありません.