こんにちは、ナナです。
ここまでのプログラムは順次処理のみで構成する一本道のストーリーでした。
でも、物語って道が分かれるから面白いんです。プログラムも色々な道を作ることで、プログラムというストーリーにバリエーションが生まれます。
本記事では次の疑問点を解消する内容となっています。
では、if文を使った分岐処理の基礎を学んでいきましょう。
if文を使ってプログラムを分岐させよう
順次処理のプログラムって単調ですね。もっと表現方法を広くすることってできないんですか?この筋肉のように重厚なプログラムを作りたいんです。
引き続きアグレッシブな子が来たね。
確かに、順次処理だけでは表現方法が乏しいよね。じゃあ、分岐処理を使ってプログラムが進む道を枝分かれさせてみようか。一番基本のif文を教えるよ。
皆さんが生活をしていく中で「もし~だったらAをしよう、そうでなかったらBをしよう」といったことはよくあるシーンですね。
プログラムにおいても「もし~なら」という分岐処理を書くことができます。
if-else文の書き方と使い方
C言語で代表的な分岐処理の記述はif文という構文を使用します。
プログラムでは次のように記述します。
書き方
if (条件式)
{
条件が成立したときに行う処理A;
}
else
{
条件が成立しないときに行う処理B;
}
「もし(条件式)が成立すればA、成立しなければB」のように処理することができます。
条件式の部分には分岐を判断するための条件式を記述します。「もし(条件式)が成立すればA 」だけでよいのであれば、else文の部分は記述を省略することができます。
条件式を書くための比較演算子
条件式には値を比較する内容を書いてプログラムの進む道を切り替えます。
値を比較するための演算子を下記に示します。 例として変数Aと変数Bを条件式にした場合の記述方法となります。
比較演算子 | 呼び方 | 条件式の書き方 | 条件式の意味 |
---|---|---|---|
< | 小なり | A < B | AがBより小さい |
<= | 小なりイコール | A <= B | AがBより以下 |
> | 大なり | A > B | AがBより大きい |
>= | 大なりイコール | A >= B | AがB以上 |
== | イコール | A == B | AとBが等しい |
!= | ノットイコール | A != B | AとBが等しくない |
条件式の判断結果は「真偽値」と呼ばれるものに変換され、「真」となった場合に条件が成立したと判断されます。
代入を示すイコールと、比較を示すイコールイコールは本当に間違えやすいから気を付けてください。何度言っても間違えちゃうんですけどね。
条件式で比較演算子のイコール(==)と代入(=)は間違えやすいため注意!
if文を使ったプログラム
実際にif文を使用したプログラムを作ってみましょう。
下記プログラムを皆さんの環境でステップ実行しながら動かしてみてください。numberの初期値やif文の条件式をいろいろ変えてみると分岐処理の動作イメージがわかるでしょう。
#include <stdio.h>
int main(void)
{
int number = 10;
if (number == 10)
{
printf("条件式が成立:真");
}
else
{
printf("条件式が不成立:偽");
}
return 0;
}
変数と数値を比較していますが、変数と変数で比較することももちろん可能です。
このように比較演算子を使うことで変数の値をチェックすることができるのです。
値の比較でしか進む道を切り替えられないのですか?
「もし、この筋肉がエネルギーを欲していたらプロテインを与える」とかっていうif文は書けないんですか?
そうだね。プログラムって結局、数値を使って処理を書いていくから、値の比較しか書けないね。だから「筋肉がエネルギーを欲していたら」を数値情報として表現できれば、その条件式は書けると思うよ。
条件式で判定される真偽値とは
if文の条件式は真偽値に変換され、結果として分岐する方向が切り替ります。
C言語による真偽値とは「真は0以外」、「偽は0」として定義されています。
よって「-1」といったマイナス値を条件式に直接記述すると、「真」として判断されることに注意が必要です。
次のように条件式に数値を直接記述しても、文法的には問題ありません。
#include <stdio.h>
int main(void)
{
// 0以外のため必ず「真」となる
if (-1)
{
printf("Hello");
}
// 0のため必ず「偽」となる
if (0)
{
printf("World");
}
return 0;
}
比較演算子の結果として「真ならば1」、「偽ならば0」の値として変換され、if文の条件式として真偽の判定が下されることになります。
では、真偽値をリアルに感じるために、次のように比較演算の結果をprintf命令で出力して確かめてみましょう。
#include <stdio.h>
int main(void)
{
int number = 100;
// 真
printf("%d\n", number == 100);
// 偽
printf("%d\n", number != 100);
return 0;
}
動かしてみた方はわかるでしょう。比較演算子の結果は1もしくは0に変換されます。
つまり、if文は次のように真偽を判定しているのです。
比較演算を行った結果が真偽値に変換され、if文による真偽の判定を行うことでプログラムの進む道が選択されます。これがif文が動く流れです。
else-if文
「もし雨なら傘を、もし台風ならカッパを、それ以外なら何も持たない」のように「もし」を順番に並べたい時もあります。
そのような場合はelse-if文を使用します。
int main(void)
{
if (天気 == 雨)
{
printf("傘をさす");
}
// else-if文で条件を追加
else if (天気 == 台風)
{
printf("カッパを着る");
}
// else-if文は複数書ける
else if (天気 == 雪)
{
printf("長靴を履く");
}
// elseは最後に一度だけ書ける
else
{
printf("何ももたない");
}
return 0;
}
else-if文はいくつでも記述できますが、else文は最後に1つだけしか書くことができない点も注意してください。
if文とelse if文の関係
if文とelse if文を並べて書く時には注意が必要です。
次のプログラムは似てはいますが動作が異なります。違いを意識して見てください。
①の場合は傘を持っていくことと、半袖にすることは両立しません。
しかし、②の場合は傘を持っていくことと半袖にすることは両立します。
条件式を並べるときはif/else-if文を組み合わせるべきか、if文を連ねるべきかをしっかりと検証しましょう。条件式を声に出して言うと間違いに気づきやすくなります。
論理演算子
「もし~」の条件には「複数の条件を同時に満たす」や「複数の条件のいずれかを満たす」といった複合パターンもあることでしょう。
そのようなときに使うのが論理演算子です。
論理演算子を使うと複数の条件式を組み合わせることができます。
論理演算子 | 呼び方 | 条件式の書き方 | 条件式の意味 |
---|---|---|---|
&& | 論理積 | 0 <= A && A < 10 | Aが0以上、かつ10より小さい |
|| | 論理和 | A < 0 || 100 < A | Aが負値、または100より大きい |
次のように論理積や論理和はif文の中に複数記述することも可能です。
書き方
if (条件式 && 条件式 || 条件式 && 条件式)
{
条件が成立したときの処理;
}
&&は||よりも判定の優先度が高いため、まず左右の&&が判定され、その後に中央の||が判定されることになります。
if文のプログラムを美しく整える方法
if文って書いているとごちゃごちゃしてプログラムが正しいのかよくわからなくなります。この筋肉のように美しい形にするにはどうしたらいいのでしょうか?
あえてここまで言わなかったけど、全部が筋肉ベースの考え方なんだね。
if文を使う時はとにかくインデントと括弧の付け方を正しく付けることだね。
インデントの使い方
if文を学んだことで「インデント」と呼ぶものを覚える必要があります。
if文のプログラムを書くときは必ずインデントを正確に記述しましょう。一般的にインデントは半角空白4つ分のサイズで表現されます。
if文を使用した時の正しいインデントの付け方は、下右図のものになります。
インデントを[Space]キー4回で打ちこんでいる方を時折見かけますが、[Tab]キーで入力するようにしましょう。
インデントってものすごく、ものすごく大事です。絶対意識してください。
インデントが正しくないプログラムはそれだけで信用を得られないです。それくらい、インデントが正しいことは基礎であり、大事なことなんです。
複数行のインデント付けテクニック
プログラムを編集するためのツールをエディタと呼びます。
Windows標準の「メモ帳」アプリもエディタの一つですが、プログラミングをするには機能として貧弱です。開発の中でプログラミングをするときは、Visual Studioのエディタのような高機能エディタを使います。
インデントを付ける際には[Tab]キーを使いますが、高機能エディタでは複数行に対してまとめてインデントをつけたり、外したりすることができます。実際に実施してみてください。
[Shift]キーは「キャンセルする」という意味合いでも結構使われるキーなんです。[Tab]をキャンセルするから左にインデントが浅くなります。
高機能エディタにはこのような開発者の面倒な作業を肩代わりしてくれる機能がたくさんあります。
エディタの使い方一つで開発効率がぐっと向上することもあります。使い方を積極的に調べるとよいでしょう。
こんな機能があることって教えてもらわないと気づきませんよね。インデントを調整する時は、このテクニックを知ってるとすごく速く調整できるんです。
if文を作るときの括弧の付け方:オススメの書き方
if文を記載する時の{}の付け方ですが、次の2つのスタイルがあります。
どちらを使うかは開発者のポリシーであり良い悪いと言えないのですが、プログラムに慣れていない方はスタイルBをオススメします。
プログラム経験が少ない方がよくやってしまうミスが括弧の付け忘れです。スタイルBのメリットは{}の始まりと終わりが綺麗に縦に並ぶことです。
スタイルBでは{}が漏れている箇所がすぐに判断できます。また、プログラムに適度に空間ができるため、可読性もよくなります。
私は初心者ではないですが、スタイルB派です。括弧が縦に並んでるのが美しいし、空間ができるのでプログラムを読むときに息苦しくないんです。
ネスト構造
if文の中に別のif文がある構造のことをネスト(入れ子)と呼びます。
if文だけに限りませんが、プログラムが右側に段を重ねることを「ネストが深い」と表現します。ネストの深さは複雑なプログラムの目安にもなり、深いことは良いプログラムではないこと示します。
ネストが深いプログラムは工夫次第で浅くすることができます。構成を見直せる技術を身につけていきましょう。
Q&A:if文に関するよくある質問
if文に関するよくある質問なんでもこいっ!
Q:if文の条件式に変数名しか書かれてないものがあった
次のように間違えて条件式に変数名しか書かなかったのに、ビルドエラーが発生しなかったんです。どうしてエラーにならないんですかっ?
筋肉が怒りに震えています!
if (number)
{
printf("Hello");
}
これはif文の条件式が真偽値としてしか判定されない、ということが理解できていれば解ける謎だね。
この書き方はC言語の文法的な観点から回答するとビルドエラーは発生しません。
なぜなら、C言語において条件式は、0または 0以外の真偽値としてしか見ていないからです。
number変数が0であれば偽と判断され、0以外であれば真と判断されます。ただし、この書き方は可読性が良くないため推奨しません。比較演算子を用いてif (number != 0) と書きましょう。
特にif (!number)というノット記号を付けた条件式を書く方がいますが、何を判定したいのか直感的に理解しづらいため if (number == 0)と書いてあげるとよいでしょう。
Q:if文で{}が付いてないものがあるが問題ないのか?
他の人のプログラムを見てたら、次のようにif文に{}が付いていないものがありました。こんな書き方習ってません、なんでですか?
筋肉はいつも嘘をつきません。
if (number == 100)
printf("Hello");
他の人のプログラムを見て観察するのは偉いね。プログラムは作るのも大事だけど読むのも大事だよ。
if文は{}を付けない書き方もあるんだけど、推奨してないから伝えなかったんだね。嘘ついてたわけじゃないんだよ、ごめんね。
C言語の文法的にはこの書き方は認められています。
しかし、皆さんは{}を省略しないようにしてください。理由としては将来的な拡張性に弱く問題を起こしやすいためです。
次のように「Hello」に続き「World」を表示しようと処理を追加したとします。
if (number == 100)
printf("Hello");
printf("World");
このプログラムは実際に{}を付けた場合では次のように解釈されます。思っていたものと異なる結果になることがわかります。
if (number == 100)
{
printf("Hello");
}
printf("World");
このような問題を起こさないためにも、実行する行が1行の場合でも{}をしっかりと付けてあげることが将来の不具合を防止することになります。
Q:条件式で代入と比較を間違えてしまう
ちょっと聞いてください。if文の条件式で間違えて代入しちゃったんです。ビルドエラーも出なくて全然間違いに気づきませんでした。
悔しくて筋肉が泣いてます。
if (num = 0) // 間違えて代入しちゃった(泣)
{
printf("Hello");
}
else
{
printf("World");
}
まぁ、よくある話だよね。何度忠告しても、やっぱり一度は痛い目に合わないと身に付かないからね。
全部が防げるわけじゃないけど防止する方法はあるよ。
この間違いは初心者のうちは本当にやらかすものです。ベテランの人たちは痛い目に何度もあってるので、経験値が上がると共にあまり見かけることがなくなります。
この問題はC言語の文法的にはエラーにならないため、気づきづらいんですね。ちょっと視点を変えることで防止することも可能です。
if (0 = num) // 左に数値、右に変数を書く
{
printf("Hello");
}
else
{
printf("World");
}
このように、数値へ値を代入することはできないので、この条件式の書き方ならビルドエラーが発生してくれます。変数同士の比較だと効果がないのが残念なんですが。
「変数は右側」ということを意識して書くと万が一間違えても気づけますね。
課題:if文による分岐の使い方が学べたかを確認しよう
もしも、プログラムが上手く動かなくて困ったときは、答えを見るのではなく「デバッガ」の使い方を学びましょう。
この記事を見ると問題の解決技術が身に付きます。困ったときのオススメ記事です!
課題1
課題内容
次の変数を定義せよ。
データ型 | 変数名 | 初期値 |
---|---|---|
int | number | (任意の値) |
numberに指定した値によりif-else文を使用して「偶数」か「奇数」かを表示せよ。表示内容は出力期待値に従い出力すること。
出力期待結果
偶数:58
奇数:71
main.c
#include <stdio.h>
int main(void)
{
int number = 58; // 偶数
// int number = 71; // 奇数
// 偶数・奇数判定
if (number % 2 == 0)
{
printf("偶数:%d", number);
}
else
{
printf("奇数:%d", number);
}
return 0;
}
偶数と奇数の区別は%による余りを判定することで可能なんです。言われてみればの使い方ですね。
課題2
課題内容
次の変数を定義せよ。
データ型 | 変数名 | 初期値 |
---|---|---|
char | score | (任意の値) |
scoreの値によって次の出力文字を表示してください。表示形式は出力期待結果に従うものとする。
点数 | 出力する文字 |
---|---|
100 | 素晴らしい! |
99~80 | よくできました |
79~30 | 頑張りましょう |
29~0 | 勉強していませんね |
その他 | 異常な点数です |
出力期待結果
score:100 素晴らしい!
score:92 よくできました
score:58 頑張りましょう
score:20 勉強していませんね
score:-18 異常な点数です
main.c
#include <stdio.h>
int main(void)
{
char score = 100;
// char score = 92;
// char score = 58;
// char score = 20;
// char score = -18;
printf("score:%d ", score);
if (score == 100)
{
printf("素晴らしい!");
}
else if (score <= 99 && score >= 80)
{
printf("よくできました");
}
else if (score <= 79 && score >= 30)
{
printf("頑張りましょう");
}
else if (score <= 29 && score >= 0)
{
printf("勉強していませんね");
}
else
{
printf("異常な点数です");
}
return 0;
}
if/else-if/else文の複合パターンです。これが書ければif文の基礎は身に付いていますね。else文は最後に1つしか書けませんよ。
課題3
課題内容
次の変数を定義せよ。
データ型 | 変数名 | 初期値 |
---|---|---|
long | year | (任意の値) |
year変数に与えた西暦によって閏年を判定し表示せよ。閏年は次の条件により判定するものとする。表示形式は出力期待結果に従うものとする。
閏年
西暦が4で割り切れる年を閏年である。
ただし、西暦が100で割り切れて400で割り切れない年は平年である。
出力期待結果
2000年:閏年
2019年:平年
2020年:閏年
2100年:平年
main.c
#include <stdio.h>
int main(void)
{
long year = 2000;
// long year = 2019;
// long year = 2020;
// long year = 2100;
printf("%d年:", year);
if (year % 4 == 0)
{
if (year % 100 == 0 && year % 400 != 0)
{
printf("平年\n");
}
else
{
printf("閏年\n");
}
}
else
{
printf("平年\n");
}
return 0;
}
少し複雑な条件式の組み合わせの問題でしたね。if文をネストさせるパターンもよく出てくるのでしっかり習得しておくとよいです。