より高度な制御
if文のネスト
if文の章で、if文はネストすることが可能であるということがわかりました
ネストとは、たとえばif文の実行ステートメント中にif文を使うことですね。しばしば入れ子とも呼ばれます
ANSI C 標準コンパイラであれば、15段階までのネストが保証されています
しかし、そこまで深いくネストすると逆にプログラムを把握しにくくなります
そのため、実際にはそれほど深いネストを見かけることはないですし、作る機会もないでしょう
通常、ネストするたびに字下げします
if (式) ステートメント;
else {
if (式) ステートメント;
else {
if ...
}
}
最初のifから式を判定して、偽であれば次のifで…それを繰り返します
全てが偽であれば最後のelseを実行して終了します
#include <stdio.h>
int main()
{
int get_int;
printf("数を入力してください\n");
scanf("%d",&get_int);
if (get_int < 1000) printf("入力された値は1000より低いです\n");
else {
if (get_int == 1000) printf("入力された値は1000です\n");
else {
printf("入力された値は1000より高いです\n");
}
}
return 0;
}
elseステートメントの中にifがネストされているのがわかりますね
しかし、この記述方で何層にもわたるネストを行なうと、字下げが深すぎて逆に可読性を失います
そのため、一般的には次のようにネストします
if (式) ステートメント;
else if (式) ステートメント;
else if (式) ステートメント;
else if ...
else ステートメント
このことを踏まえて上のプログラムを書き換えると以下のようになります
#include <stdio.h>
int main()
{
int get_int;
printf("数を入力してください\n");
scanf("%d",&get_int);
if (get_int< 1000) printf("入力された値は1000より低いです\n");
else if (get_int == 1000) printf("入力された値は1000です\n");
else {
printf("入力された値は1000より高いです\n");
}
return 0;
}
このような複雑なif文の場合、elseがどのif文に対応しているのか見失わないようにして下さい
論理演算子を用いた条件式
これまでの条件式では、主に関係演算子を用いてきましたが、ここで論理演算子について触れたいと思います
論理演算子は二つのブール式を結合させます
ブール式とはすなわち真か偽かですね。この仕組はすでにわかっていると思います
まず、&&(論理積)から見てみましょう
&& は二つの式の評価が真の場合のみ真を返します
真とはすなわち0以外でしたね、つまり一方の式の評価が偽(0)だった場合には偽を返します
#include <stdio.h>
int main()
{
int bool1,bool2;
printf("0か1を入力してください\n");
scanf("%d",&bool1);
printf("ANDで評価します、0か1を入力してください\n");
scanf("%d",&bool2);
printf("答えは %d です",(bool1) && (bool2));
return 0;
}
ひとつの式の値が真か偽かを、単刀直入に入力して && で評価します
printf関数の第二引数でダイレクトに結果を表示するようになってますね
考えられる真と偽のパターンを入力して実験してみましょう
1と1、つまり真と真であった場合のみ1が返されることに気づいてください
双方の式が成立した場合にのみ真を返す、つまりANDなのです
つぎに論理和(OR)演算子 || を学習しましょう
これは一方に真があれば真を返します
逆にいえば、双方が偽の場合のみ偽を返します
#include <stdio.h>
int main()
{
int bool1,bool2;
printf("0か1を入力してください\n");
scanf("%d",&bool1);
printf("ORで評価します、0か1を入力してください\n");
scanf("%d",&bool2);
printf("答えは %d です",(bool1) || (bool2));
return 0;
}
一方に真(0以外)を入力するとORは1を返します
もちろん双方が真でも1を返します
最後は比較的多用される ! 演算子です
「結果の否定」 すなわちNOTを行ないます
! は指定された式の評価が真であれば偽に、偽であれば真にします
もちろん && や || 演算子と一緒に用いることが可能であり、そうすることで威力を発揮することもあります
#include <stdio.h>
int main()
{
int bool1;
printf("NOTで否定します、0か1を入力してください\n");
scanf("%d",&bool1);
printf("答えは %d です",!(bool1));
return 0;
}
0を入力すれば1が、0以外を入力すれば0が出力されます
実際にプログラムして試してみてください
ではここで問題です
一方が真の場合のみ真を返すという論理演算はどのようにすればよいでしょう
|| 演算子 OR は、双方が真の場合でも真を返しました
そうではなく、一方が真の場合のみ真を返すのです。
この論理演算を排他的論理和 XORといいます
XOR はこれまでの3つの論理演算子を用いて代用することができます
二つのオペランドのうち一方が真であることを確認し
双方が真であれば否定して偽にすればよいのです
一方が真であれば真というのは論理和 bool1 || bool2
双方が真であればというのは論理積 bool1 && bool2 ですね
そして NOT で AND を否定した結果と最初の OR の結果を AND で評価します
(bool1 || bool2) && !(bool1 && bool2)
以下のプログラムはXORを作り、評価の結果を出力します
二つのオペランドを入力し、一方のオペランドが真の場合のみ真(1)を返します
#include <stdio.h>
int main()
{
int bool1,bool2;
printf("0か1を入力してください\n");
scanf("%d",&bool1);
printf("XORで否定します、0か1を入力してください\n");
scanf("%d",&bool2);
printf("答えは %d です",(bool1 || bool2) && !(bool1 && bool2));
return 0;
}
論理式は必ず0か0以外かで評価されます
これを理解することで、より高度で完成度の高いフロー制御文を記述できます
ところで、論理積と論理和を用いるとショートカットという現象が発生します
ショートカットは、最低限必要な演算のみを行い、不必要な計算を省略する仕組みです
たとえば、 論理積では左辺のオペランドが偽の場合は右辺に関係なく偽になります
また、論理和では左辺のオペランドが真であれば右辺に関係なく結果は真です
この減少は、普段は意識する必要はありませんが
論理演算において、評価時に何らかの処理を発生させる場合は注意が必要です
例えば、右オペランドにインクリメントやデクリメント演算子をしていしている場合などです
空の式を持つfor文
for制御は、高度なプログラムになってくればさまざまな用途が考えられます
そのひとつとして、for文の変数の初期化屋敷は省略することができるという柔軟性です
for文のリファレンスにも省略可能であると書いておきました
たとえば以下のプログラムは変数の初期化を省略したforステートメントです
#include <stdio.h>
int main()
{
int count;
count = 0;
for ( ; count <= 10 ; count++) {
printf("%d回目のくり返しです\n",count);
}
return 0;
}
もちろん、いっそのこと全てを省略してしまうことも可能です
#include <stdio.h>
int main()
{
int count;
count = 0;
for (;;) {
count++;
printf("%d回目のくり返しです\n",count);
if (count >= 10) break;
}
return 0;
}
for文は文字列操作などによく活用されます、forによる柔軟なループ制御のスキルは大切です
これまでの制御文や演算子を活用して、さまざまなプログラムを試してください
ループの先頭に復帰する
ループから抜け出す専用のステートメント break はswitchで学習しました
breakは、forやwhileステートメントの中で指定することでループから抜け出します
しかし、breakとは対称にループの先頭に復帰するcontinueステートメントが存在します
continueは、たとえば入力された値が不正だった場合にループの先頭に復帰して
入力をユーザーにやり直させたい場合などに使用されます
#include <stdio.h>
int main()
{
int var;
do {
printf("値を入力してください\n");
scanf("%d",&var);
if ((var <= 0) || (var >= 10000)) {
printf("不正な値です\n");
continue;
}
printf("入力を受け付けました\n");
break;
} while (1);
return 0;
}
このプログラムは、最初に値の入力をユーザーに求めます
入力された値が0以下か10000以上であれば、continueで do の先頭まで復帰します
このようにプログラムすることで、確実にプログラマの意図した範囲の値を得ることができますね
このときcontinueはループステートメントの式の評価部分まで戻ります
実行ステートメントの先頭でないことに注意してください
(Perl言語でいう next ステートメントにあたります)
continue
ループステートメントの中で用います
continueが実行されると、ループステートメントの先頭にもどることができます