if文

今回勉強していくのはif文です。いわゆる制御構文と言われるものでものすごく重要です。最初はプログラミング特有の構文に戸惑うかもしれませんが、ひとつずつ学んでいけば問題なく理解できるはずなのでじっくり読んでみてくださいね。

前回の復習

前回はscanf関数を用いた入力についてみていきました。scanf関数では『アドレス演算子(&)』を用いてメモリ上に直接アクセスして値を格納しているんでしたよね。

また、scanf関数の中で用いるフォーマット指定子はprintf関数のときに使ったものと少し異なっていて、それぞれ入力用と出力用があることもみました。

また配列は参照型と呼ばれるタイプなので、アドレス演算子をつけなくても初めからアドレス値が格納されていることについても触れました。

今回は話題がガラリと変わって条件分岐の構文であるif文を学びます。その前に前回の練習問題の解答を見ていきましょう!

前回の練習問題の解答例

A問題

亀がn匹、鶴がm羽います。亀の足は4本、鶴の足は2本です。この場には亀と鶴以外はいません。このとき、この場にいる生き物の頭の数と足の数を出力してください。入力はn, mの順で与えられます。自分で入力や出力をして試してみてください。

入力

n m

入力例1

2 3

出力例1

5 14

亀が2匹、鶴が3羽いるので、頭は5で足は14になります。


入力例2

100 25

出力例2

125 450

解答例

#include <stdio.h>

int main() {
  int n, m, head, leg;
  scanf("%d %d", &n, &m);
  head = n + m;
  leg = 4 * n + 2 * m;
  printf("%d %d\n", head, leg);
  return 0;
}

頭はどちらの生き物も1つしかないのでn匹とm羽の場合、頭の数はn + mになります。一方で、足の数はn匹とm羽の場合、足の数は4 × n + 2 × mで求められます。

また、足の数が一般化された場合も考えてみると面白いでしょう。(『亀の足はk本で鶴の足はt本、n, m, k, tは入力として与えられる』みたいな問題設定とか)

B問題

岩ちょこ君は奇数が大嫌いです。奇数なら1を足して偶数にしてください。ただし入力される数字は0以上100以下であることが保証されます。
入力

n

入力例1

2

出力例1

2

偶数はそのまま出力します。


入力例2

25

出力例2

26

奇数は+1して偶数にします。


解答例

#include <stdio.h>

int main() {
  int n;
  scanf("%d", &n);
  n += n % 2;
  printf("%d\n", n);
  return 0;
}

少しだけ工夫した書き方をしていますが、ソースコードを見ればなんとなく理解できるでしょう。6行目について少し触れておくと、n % 2の部分はnが奇数なら1偶数なら0となりますよね? その値をnに足してあげるという処理をしています。

今回勉強するif文を習得すれば工夫しなくてもかけますが、頭を使った練習問題に挑戦してみて欲しかったので、あえてif文を勉強する前に出題してみました。

比較演算子と論理演算子

第5回の演算子の記事で残していた比較演算子論理演算子についてまずは勉強していきます。

演算子の種類分け 具体例
比較演算子 ==, !=, <, >, <=, >=
論理演算子 &&, ||, !

比較演算子

比較演算子というのは数学でもお馴染みの『大なり』とか『小なり』とかいうやつですね。

条件演算子というのは、その式が実際に成立していれば『1』に、成立していなければ『0』という値をかえす演算子になります。(他の言語でいうところの1はtrue, 0はfalseに当たります。C言語以外はほとんどtrue/falseなので頭の片隅に入れておくと良いかもしれません)

文章で言われても実感がわかない人もいるかと思いますので、サンプルコードを実行してみましょう!

#include <stdio.h>

int main() {
  int a = 2, b = 0;
  printf("%d\n", a > b); //a > b が成り立っていれば1が, 成り立っていなければ0が出力される
  return 0;
}

実行結果

1

今回のソースコードではわかりやすいようにa, bの変数を初期化して書いていますが、scanf関数を用いて書いておくといちいちaとbの値を変えるたびにコンパイルし直す手間がなくなるのでscanf関数で書いて実験してみると良いと思います。

上記のソースコードでは、a > bが成り立っているので無事に『1』が出力されました。

このように成り立っているか否かを判定することができるのが条件演算子になります。具体的な演算子については以下の表を見て貰えばなんとなくイメージできると思います。

演算子 構文 備考
== a == b aとbが等しいかを判定し、等しい場合に『1』を等しくない場合には『0』をかえす
!= a != b aとbが等しくないかを判定し、等しい場合に『0』を等しくない場合には『1』をかえす
< a < b aがbより小さいかを判定し、成り立つ場合に『1』を成り立たない場合には『0』をかえす
> a > b aがbより大きいかを判定し、成り立つ場合に『1』を成り立たない場合には『0』をかえす
<= a <= b aがb以下かを判定し、成り立つ場合に『1』を成り立たない場合には『0』をかえす
>= a >= b aがb以上かを判定し、成り立つ場合に『1』を成り立たない場合には『0』をかえす

注意して欲しいのは、等しいかどうかを判定する比較演算子はイコールが2つ必要なことです。最初の頃に頻発するミスなので意識しすぎるくらい気を付けておきましょう。イコール2つです(念押し)。

比較演算子を用いて判定をしている部分条件式と呼びます。この後の論理演算子の項やif文の項目でも登場する用語なので押さえておきましょう。

#include <stdio.h>

int main() {
  int a = 2, b = 1;
  printf("%d\n", a == b);
  printf("%d\n", a != b);
  printf("%d\n", a > b);
  printf("%d\n", a < b);
  printf("%d\n", a >= b);
  printf("%d\n", a <= b);
  return 0;
}

実行結果

0
1
1
0
1
0

論理演算子

こちらは少し厄介かもしれません。AND(かつ)とかOR(または)とかが分かる人なら何も問題はないのですが、集合やベン図を知らない人は少し大変かもしれません。なのでわかりやすい例を交えながら説明していくことにします。

例えば次のような条件を考えてみましょう。私の理想の恋人は、『背が高く』て、『お金持ち』で、『優しい人』です。この文面ではすべての条件を満たしていれば理想の恋人ということになりますよね。このように複数の条件を満たしているかを判定するものANDという論理演算子で表現することになります。

一方で、明日の天気が『晴れ』か『曇り』なら動物園に出かけようかな。というような文面ではどちらか片方の条件を満たしていれば動物園に出かけることになりますよね。このように複数の条件のうちひとつだけでも条件を満たしているかを判定するものORという論理演算子で表現することになります。

なんとなくイメージできたと思うので、実際にANDとORをプログラムでどう表現するのかを表とサンプルコードで確認してもらいます。
ただしここでaとbは比較演算子を用いた条件式を表しているとします。

演算子 構文 備考
&& a && b aとbの両方の条件式が成り立っている場合に『1』をどちらか一方でも成り立っていない場合には『0』をかえす(AND, 論理積)
|| a || b aとbのどちらか一方でも成り立っている場合に『1』を両方とも成り立っていない場合には『0』をかえす(OR, 論理和)
! !a aが『0』の場合に『1』をaが『1』の場合には『0』をかえす(NOT, 否定)

論理演算子を用いて複数の条件式をまとめたものも条件式と呼びます。(最終的に『0』か『1』を返すものは条件式と呼ぶものだと考えても良いでしょう)

また否定をうまく使うことでNANDやNORやXORのような条件式を作ることができるので覚えておきましょうね。

#include <stdio.h>

int main() {
  int a = 2, b = 1, c = 3;
  /*一番大きい値のところに『1』が立つ*/
  printf("大きい値はどれだ\n");
  printf("a:%d\n", (a >= b) && (a >= c));
  printf("b:%d\n", (b >= a) && (b >= c));
  printf("c:%d\n", (c >= a) && (c >= b));

  /*一番小さい値のところに『0』が立つ*/
  printf("小さい値はどれだ\n");
  printf("a:%d\n", (a >= b) || (a >= c));
  printf("b:%d\n", (b >= a) || (b >= c));
  printf("c:%d\n", (c >= a) || (c >= b));
  return 0;
}

実行結果

大きい値はどれだ
a:0
b:0
c:1
小さい値はどれだ
a:1
b:0
c:1

a, b, cの値が互いに異なる場合はちゃんと動くことが確認できますね。

条件式とは、比較演算子や論理演算子を組み合わせて作られた式のことで、条件が成り立っている場合には『1』を、成り立っていない場合には『0』を返す。

if文

条件演算子と論理演算子を使った条件式の勉強が済んだところで、今回の記事のメイン部分であるif文についての説明をしていきます。if文を使うことで何ができるのかというと処理を分岐させることができます。まずはif文のイメージを掴んでもらうために簡単な例を用いて条件分岐について説明していきます。

次のような話を考えてみましょう。明日の天気が晴れならば遊園地に行くことにし、雨ならば水族館に行くことにします。このようにあるもの(値)をうけて行動(処理)が変わるもの条件分岐と言います。条件分岐をプログラム中で表現するために用いられるのがif文です。

それではif文の書き方をみていきましょう。

if(条件式) {
  処理1;
  処理2;
}

条件式が成り立っている(『1』になる)場合に後ろの波括弧内の処理が実行されます。

サンプルコードも載せておきます。数値の偶奇で処理を変えたい場合は以下のように『2で割ったあまりが1か0か』で判定します。

#include <stdio.h>

int main() {
  int number;
  scanf("%d", &number);
  printf("入力された値は%dです。\n", number);
  if(number % 2 == 0) {
    printf("偶数大好き!!\n");
  }
  return 0;
}

実行結果

12 //入力値
入力された値は12です。
偶数大好き!!
51 //入力値
入力された値は51です。

このように入力された値が偶数の時のみif文の波括弧が実行されます。そのため奇数を入力した場合にはif文のところの波括弧内にあるprintf関数が実行されていないのが分かると思います。

ちなみに今回のように処理するものが1文しかない場合には波括弧を省略して書くこともできます。他の人のコードを読んだときにびっくりしないように頭の片隅にでも入れておくと安心!自分がコードを書くときはあんまり気にしなくても大丈夫でしょう。

#include <stdio.h>

int main() {
  int number;
  scanf("%d", &number);
  printf("入力された値は%dです。\n", number);
  if(number % 2 == 0) printf("偶数大好き!!\n"); 
  return 0;
}
if文というのはプログラム中で処理を分岐させたいときに用いる構文である。基本構造は以下のように書き、処理が1つしかない場合は波括弧を省略して書くことができる。

if(条件式) {
  処理1;
  処理2;
}

if-else文

if文について理解を深める意味もかねて、そのまま一緒にif-else文についても勉強してもらいます。

先ほどまで見てきたif文を使うことで『条件式が成り立っているならば処理1を行う』ということが可能になりました。今度はここに少し付け足して、『条件式が成り立っているならば処理1を行い、成り立っていないならば処理2を行う』というプログラムの書き方を勉強してもらいます。

とりあえず以下のサンプルコードを眺めてみてください。

#include <stdio.h>

int main() {
  int number;
  scanf("%d", &number);
  printf("入力された値は%dです。\n", number);
  if(number % 2 == 0) {
    printf("偶数大好き!!\n");
  } else {
    printf("奇数好きくない...\n");
  }
  return 0;
}

先ほどまでのif文のサンプルコードの後ろに何やら見慣れないものが付け足されていますね(9行目から11行目の部分)。

ここの部分が『成り立っていないならば…(略)』の部分に相当しそうだなと思った方はなかなか勘がいいですね!まさにその通りで、if-else文の基本構造は以下のように2つの波括弧のブロックがあります。

if(条件式) {
  処理1;
} else {
  処理2;
}

条件式が成り立っているときは条件式のすぐ後ろのブロックを、成り立っていないときはelseのすぐ後ろのブロックを実行します。

慣れるためにサンプルコードを書いて実験してみましょう。

#include <stdio.h>

int main() {
  int score;
  scanf("%d", &score);
  if(score >= 60) {
    printf("合格!!\n");
  } else {
    printf("不合格!!\n");
  }
  return 0;
}

実行結果

50 //入力値
不合格!!
70 //入力値
合格!!

scoreが60以上のとき最初の波括弧部分が実行されて、scoreが60未満のときはelseの後ろの波括弧が実行されているのがわかりますね。

このように成り立っている場合と成り立っていない場合の処理をそれぞれ記述するためにはif-else文を使うのが良いでしょう。

if-else文は以下のように書くことで2つ以上の分岐ができます。

#include <stdio.h>

int main() {
  char weather; //Sは晴れ, Cは曇り, Rは雨
  scanf("%c", &weather);
  if(weather == 'S') {
    printf("晴れ!!\n");
  } else if(weather == 'C') {
    printf("曇り!!\n");
  } else if(weather == 'R') {
    printf("雨!!\n");
  } else {
    printf("不明\n");
  }
  return 0;
}

実行結果

S //入力値
晴れ!!
C //入力値
曇り!!
R //入力値
雨!!
A //入力値
不明

処理の流れは、1つ目の条件式(weather == ‘S’)が成り立っていたらそのすぐ後ろにある括弧を実行し、成り立っていない場合はelseに移動してそこにあるif文を実行する処理をしています。

if-else文の場合も処理が1文しかない場合には、波括弧を省略して書くことができます。

#include <stdio.h>

int main() {
  char weather; //Sは晴れ, Cは曇り, Rは雨
  scanf("%c", &weather);
  if(weather == 'S') printf("晴れ!!\n");
  else if(weather == 'C') printf("曇り!!\n");
  else if(weather == 'R') printf("雨!!\n");
  else printf("不明\n");
  return 0;
}

実行結果

S //入力値
晴れ!!
R //入力値
雨!!
if-else文というのは条件式が成り立たなかった場合にも処理を行いたい場合に用い、通常のif文の後ろにelseと波括弧を付け足した形をしている。

if(条件式) {
  処理1;
} else {
  処理2;
}

また、if-else文は以下のように連続して書くこともできる。

if(条件式1) {
  処理1;
} else if(条件式2) {
  処理2;
} else {
  処理3;
}
column
『if-else文がいくつも連続して書けるのはどうして?』という疑問について深い話をしようと思ったのでコラム扱いにしています。知らなくてもぶっちゃけ何にも問題ありません。余裕がある人はどうぞ。

if-else文が連続して、以下のように書ける原理を説明していきます。

if(条件式1) {
  処理1;
} else if(条件式2) {
  処理2;
} else if(条件式3) {
  処理3;
} else if(条件式4) {
  処理4;
}

これは簡単にいうとif-else文の処理が1文で書ける場合は波括弧が省略できるというルールがうまく適用されているためにできた文法です。そのためコンパイラの中にif-else-if-elseみたいな文法は特別定義されていません。たまにelse ifという文法があるかのように書かれているものも散見されますが、厳密にはそんな文法は定義されてないよという小咄ですね。

このC言語講座の第3回目で文という概念について触れたのを覚えていますか? 文というのは、波括弧で囲まれたものやセミコロン(;)で終わっているものを指すんでしたよね。すなわち、if文の構造をこの形が基本だよという話を上の方でしましたが、実のところはコンパイラの中で定義されているif文の基本構造は以下のようになります。

if(条件式) 文

同様にif-else文も以下のように定義されています。

if(条件式) 文
else 文

ここで、文という概念で書かれた部分をそれぞれ波括弧やif文, if-else文で書き換えてみてください。(記事中で基本形と呼んでいた形に出来たと思います)

これによってif-elseを連続して書くことが可能となっているわけですね。

switch文

最後にif文以外で条件分岐ができる構文でswitch文を紹介しておきます。switch文の基本的な形は以下のようになります。

switch(式) {
  case 値1:
    処理1;
    break;
  case 値2:
    処理2;
    break;
  case 値3:
    処理3;
    break;
  case 値4:
    処理4;
    break;
  default:
    処理5;
    break;
}

『case 値:』の部分は1つ以上あれば大丈夫です。この文法を用いることで、どんなコードがかけるのかを知るためにサンプルのソースコードをみてもらいましょう。

#include <stdio.h>

int main() {
  int number;
  printf("0~10で好きな数字を入力してください: ");
  scanf("%d", &number);
  switch(number) {
    case 0:
      printf("大吉!!\n");
      break;
    case 1:
    case 2:
      printf("中吉!!\n");
      break;
    case 3:
      printf("吉!!\n");
      break;
    case 4:
    case 5:
      printf("凶!!\n");
      break;
    default:
      printf("大凶!!\n");
      break;
  }
  return 0;
}

実行結果

0~10で好きな数字を入力してください: 0 //入力
大吉!!
0~10で好きな数字を入力してください: 1 //入力
中吉!!
0~10で好きな数字を入力してください: 3 //入力
吉!!
0~10で好きな数字を入力してください: 5 //入力
凶!!
0~10で好きな数字を入力してください: 8 //入力
大凶!!

このプログラムの7行目から25行目の部分について説明していきます。この部分は一言で表すと条件分岐をしている部分になります。原理についてはサンプルコードを眺めつつ説明していきます。

まず7行目をみてください!switchのすぐ後ろにある丸括弧の中にnumberという変数が書かれています。その後に続く波括弧の中に『case 値:』というものと『break;』というものが見えます。これは、numberの値がcaseの後ろに書いた数値と一致していたらその直後から処理を行い、break;が書かれているところまで来たらswitch文の波括弧から抜け出してswitch文の下からプログラムが再開されるという流れになっています。

また、今回のようにnumberの値が1の時と2の時の処理を同じにしたい場合、11行目と12行目のように『case 値:』を連続して書くことで同じ処理を行わせることができます。caseの後ろに値を書いてその後にコロンをつけるようにします。(セミコロンではないので注意!)

注意点としてはbreak文を書き忘れるとそのまま下のcaseの場合も実行されてしまうので、書き忘れないように気をつけましょう!あえてbreakを書かない書き方が許されているため、コンパイルエラーが出なくて気付きにくい間違いを引き起こします……。

caseで書いたどの値にも当てはまらない場合はdefaultの部分が実行されます。うまく処理をまとめてその他の部分はこれを使ってまとめて処理しましょう。

『default:』の部分は無理に実装することはないです。不要な場合は書かなくても大丈夫です。しっかり動きます。

ちなみに、tc0908.cのサンプルコードをswitch文ではなくif-else文を用いて書くと以下のようになります。

#include <stdio.h>

int main() {
  int number;
  printf("0~10で好きな数字を入力してください: ");
  scanf("%d", &number);
  if(number == 0) {
    printf("大吉!!\n");
  } else if(number == 1 || number == 2) {
    printf("中吉!!\n");
  } else if(number == 3) {
    printf("吉!!\n");
  } else if(number == 4 || number == 5) {
    printf("凶!!\n");
  } else {
    printf("大凶!!\n");
  }
  return 0;
}
switch文とは条件分岐を記述できる構文で、caseとbreakを用いて書くことができる。また条件演算子の==のみを用いたif文であれば書き換えることができる。
column
条件分岐ができる演算子として、『条件演算子(?)』を紹介します。使い方は簡単で以下のような形で用います。

条件式? 処理1 : 処理2

条件式が成り立っている場合には処理1が、成り立っていない場合には処理2が実行されます。実際に使ってみましょう。

#include <stdio.h>

int main() {
  int a, b;
  scanf("%d %d", &a, &b);
  printf("%dの方が大きい\n", a > b? a : b);
  return 0;
}

こういう講座記事で取り上げられることは滅多にない演算子ですが覚えておくと短いコードが書けるので知っておくと良いでしょう。

まとめ

今回の記事ではif文を用いた条件分岐を中心に勉強しました。条件式というものからif文, if-else文, switch文について結構盛り沢山な記事でしたね。(・_・;

今回の内容をざっとまとめておきます。各構文の書き方は記事中の該当箇所を見直してくださいね。

  • 条件式とは条件演算子と論理演算子を組み合わせて作られた式のことで、『0』または『1』の値をとる。条件式はif文やif-else文などに用いられる。
  • 条件分岐を実装できる構文にはif文, if-else文, switch文の3つのみである。

次回はfor文について勉強していきます。次回の記事も分量が多くなりそうですが頑張っていきましょう。適宜復習をしつつ自分のペースで読み進めてもらえれば幸いです。

練習問題

A問題

岩ちょこ君は数字の書かれたカードを3枚持っています。1枚目のカードに書かれた数字はa, 2枚目のカードに書かれた数字はb, 3枚目のカードに書かれた数字はcです。カードに書かれた数字で一番大きい数字はいくつですか。またその数字が書かれたカードは何枚目ですか。

a, b, cの値は互いに異なることが保証されているものとする。

入力

a b c

出力

m t

mには一番大きな数字を、tには何枚目のカードであるかを出力してください。


入力例1

1 2 3

出力例1

3 3

一番大きな数字は3で、その数字が書かれたカードは3枚目です。


入力例2

1 50 25

出力例2

50 2

一番大きな数字は50で、その数字が書かれたカードは2枚目です。

B問題

岩ちょこ君は街Aから街Bを経由し街Cに行きます。歩いて行くのが大変なので、バスか電車のどちらかを用いて移動をすることにしました。街Aから街Bに行くのにバスを使うとb_1円、電車を使うとt_1円かかり、街Bから街Cに行くのにバスを使うとb_2円、電車を使うとt_2円かかります。岩ちょこ君はお金をなるべく使いたくありません。あなたは岩ちょこ君のわがままを叶えるための最適な交通手段を彼に代わって作成してください。
入力

b_1 t_1
b_2 t_2

入力例1

20 100
60 40

出力例1

60

最も安い運賃はAからBでバスを使い(20円)、BからCで電車を使った(40円)場合です。


入力例2

25 4000
39000 1000

出力例2

1025

ex問題

ABC 002 A 正直者【AtCoder】
ABC 006 A 世界のFizzBuzz【AtCoder】
ABC 002 C 直訴【AtCoder】
ABC 003 B AtCoderトランプ【AtCoder】

scanf関数【C言語講座#8】

for文【C言語講座#10】


最後まで記事を見ていただきありがとうございます。また別の記事でお会いできることを祈っております。

Print Friendly, PDF & Email

C言語カテゴリの最新記事