ログイン
kid's world

条件分岐のif文

条件分岐のif文

さて条件式の作り方を学んだところで,いよいよ制御文の解説に入ろう。

はじめに解説する制御文はif文である。if文は,値によって実行する文を切り替えてくれる便利な制御文だ。その構文をFig.1に示そう。

Fig. 1 if文の構文

if(式)
  文1
else
  文2

if文は「式」が0のときに文2を,それ以外のときに文1を実行する。文1か文2を実行したあとは,それ以降の文を実行し,文1と文2が続けて実行されることはない。ちなみにelse以下は省略して,文1のみを書くこともできる。

List 2は,入力された値によって表示を変える例である。いろいろな値を入力してみて,その動作を確認していただきたい。

List 2: if文の簡単な例

#include <stdio.h>

main()
{
  int i;
  
  scanf("%d", &i);
  
  if(i)
    printf("iは0以外です。\n");
  else
    printf("iは0です。\n");
}

さて,ここからが本題だ。if文は0とそれ以外の値で実行する文を変えてくれるのだから,関係演算子や論理演算子を使った式を与えたらどうなるだろう。……そう,条件が真のとき文1,偽のとき文2が実行されるようになるわけだ。

仮に,歩く関数walkと,タクシーに乗る関数taxiがあったとしよう。このとき次のようなif文を書けば,「残り時間tが100以上かつ降水量rが0のときに歩き,そうでなければタクシーを使う」ことができるようになる。

if (t>=100 && r==0)
  walk();
else
  taxi();

ブロックで1つの文に見せかける

さて,if文は文1か文2のどちらかを条件によって実行する制御文だということはわかった。しかし,いざ自分でプログラムを作ってみると,そのうち不便だと思うはずだ。

何が不便か。「文1」などというからには「printf("Hello!\n");」などの文を1つしか書けず,2つ以上の文を書きたいと思ってもできないのだ。これはいくらなんでも不便だ。むりやり複数の文を書こうとして,次のように書くのは誤りだ。

if (t>=100 && r==0)
  printf("歩きます。\n");
  walk();
else
  printf("タクシーに乗ります。\n");
  taxi();

そこでその不便さを解消すべく,1つの文しか書けないところに複数の文を書くテクニックを紹介しよう。それはブロックを使ったテクニックだ。

ブロックは「{」と「}」で成り立ち,その中にはいくつも文を書くことができる。文法上はブロックは1つの文とみなされる。main関数でも同じ記号を書くが,これはちょっと性質が違っていて,1つの文しか書かないときはブロックにしなくてもいい,というわけにはいかないので注意しよう。

先ほどの例は次のようになる。

if (t>=100 && r==0)
{
  printf("歩きます。\n");
  walk();
}
else
{
  printf("タクシーに乗ります。\n");
  taxi();
}

ただ,このように書くと行が間延びしてしまうので,次のような書き方が好んで使われている。

if (t>=100 && r==0) {
  printf("歩きます。\n");
  walk();
}
else {
  printf("タクシーに乗ります。\n");
  taxi();
}

ところで上の例は,わざと文の書く位置をずらしている。こうすると「ブロック内の文だ」ということが見てすぐにわかって,制御の流れを目で追いやすくなる。これを全部同じ位置にそろえてしまうと,とても見にくいプログラムになってしまう。よって「{がきたら右にインデントする」という習慣をつけておくとよいだろう。

if文をネストする

if文も,しょせんは文である。だから,if文の「文1」や「文2」の場所にif文を書いてしまっても,なんら問題はない。List 3に例を示そう。縦棒部分がif文の中に書いたif文である。この4行が1つの文として解釈されるので,文法上はまったく問題ないのである。

List 3: if文のネスト

#include <stdio.h>

main()
{
  int i=100, j=150;
  
  if (i>50) 
|   if (j>100)
|     printf("iは50よりも大きく、jは100よりも大きい。\n");
|   else
|     printf("iは50よりも大きいが、jはは100以下である。\n");
  else
    printf("iは50以下である。\n");
}

ところで,わざとこの4行を右にインデントしていることに注意しよう。印刷ミスではない。このように書くことで,文法的な区切りを視覚的に表現することができるのだ。インデントのしかたにはある程度の法則があるが,プログラマごとに習慣が異なる場合もあるので,自分が見やすいと思った方法で慣れればよいだろう。

さて,次はList 4に注目していただきたい。まずはプログラムの実行結果を予測してみよう。

List 4: if文の誤ったネスト

#include <stdio.h>

main()
{
  int i=101, j=200;

  if (i==100)
    if (j==200)
      printf("iは100で、jは200です。\n");
  else
    printf("iは100ではありません。\n");
}

あなたはどのような予想をしただろうか。「iには101が入っているから,100ではないという旨のメッセージが表示されるのではないか」と,ほとんどの読者は思ったのではないだろうか。筆者もこのコードをぱっと見せられたら,最初はそう思うかもしれない。

では実際に実行してみよう。……画面に何も表示されないからといって,パソコンをたたかないように。故障したわけではない。これで正しい動作だ。何も表示されないというのはかなり驚くべき結果だ。どうしたというのだろう。なぜ何も表示されないのだろうか。

その答えのヒントは,iに100を,jに201を入れればわかるだろう。なんと,iは100なのに「iは100ではありません。」と表示されるのだ。原因はわかっただろうか? まだ答えがわからない人は,List 5を見よう。

List 5: List 4の変形

#include <stdio.h>

main()
{
  int i=101, j=200;

  if (i==100)
    if (j==200)
      printf("iは100で、jは200です。\n");
    else
      printf("iは100ではありません。\n");
}

List 4とList 5は,まったく同じプログラムである。違うのはインデントの幅だけでそれはコンパイラに無視されるということは,以前に学んだことである。そう,このように最後のelseは,2番目のif文のelseとして解釈されるわけである。なぜならifからelseすべてが1つの文となるからである。

それでは,elseを1番目のif文と対応づけるにはどうしたらよいのだろうか。これは,ブロックを使えばうまくいく。2番目のif文をブロックでくるんでしまって,elseと切り離すのだ(List 6)。

List 6: List 4の修正版

#include <stdio.h>

main()
{
  int i=101, j=200;

  if (i==100) {
    if (j==200)
      printf("iは100で、jは200です。\n");
  }
  else
    printf("iは100ではありません。\n");
}

今回の例はハマると非常に抜け出しにくいミスだが,このような状態に陥ったときは,先入観を捨ててコンパイラの気持ちになってソースを吟味してみる必要がある。しかし,何度見直してもだめだった場合は,その場を離れよう。コーヒーを飲みにいくのもよし,散歩しに外へいくのもよし,ともかくコンピュータから離れよう。そうやってリフレッシュしたあとにもう一度問題のプログラムを見ると,意外とすんなりとミスが発見できるものだ(しかもびっくりするぐらい簡単なミスで,恥ずかしく思うことも少なくない)。

たくさん失敗を重ねていけば,このような失敗は少なくなっていく。そしてどんどんソースを組む時間が短縮されていくものだ。


多分岐を実現するswitch文」へ進む

広告


©Toshio Koide 1996-2007.

目次

リンクについて

リンクは御自由にどうぞ。

メール

mail.gif

広告