前回に引き続き,さまざまな制御文の使い方について学ぼう.
単純なif(...)
文,二者択一をおこなうif(...) - else
文に加えて,
より多くの条件分岐・場合分けを行いたい場合には,その間に複数のelse if(...)
文を追加する事によって,多分岐を表現することができる.
書き方のルール
if(...)
と,最後の else
は,それぞれ最初と最後に1つ置くelse if(...)
は,いくつあってもよい.この構文を用いれば,任意の数の場合分けが表現可能である.
重要: 条件式の判定は,「式1」,「式2」,...,「式n-1」の順に上から順番に評価され,一つが「真」と判定されると,そのif
文全体が終了し, それ以降の条件式は判定が行わない.
したがって,意図した動作をさせるためには,条件式の記述順序に注意する必要がある.
if(式1) {
文1; // 式1が「真」の場合,文1を実行し,<ここ>にジャンプ
} else if(式2){
文2; // 式2が「真」の場合,文2を実行し,<ここ>にジャンプ
・
・ // else if はいくつあっても良い.
・
} else if(式n-1){
文n-1; // 式n-1が「真」の場合,文n-1を実行し,<ここ>にジャンプ
} else {
文n; // すべての式が「偽」の場合,文nを実行し,<ここ>にジャンプ
} // <ここ>
if() ... else if() ... else
構文を用いて,
n
の値をキーボードから入力する.その値が
n
の値をキーボードから入力し,3, 5 の倍数であればそれぞれ「multiple of 3」「multiple of 5」と表示し,それ以外であれば「Others」と画面に表示せよ.if(), else if()
の式の順序を変えて,動作の変化を確認)
制御文は多重な構造とすることが可能である.
例えば,if
文の中に,別の if
やfor
を入れることもできる.
if
文が,別の if
や else
の中に入っている時,外側の if
にネストされた if
とか,入れ子の if
などと呼ぶ.
if( (class == 5) || (class == 6) ) { // もしクラスが 5組 or 6組 だったら
printf("Student of Mechanical Engineering\n");
if(class == 5) { // 入れ子のif
printf("Room is A201\n");
} else if(class == 6) {
printf("Room is A202\n");
}
}
ネストされた if
文では,一般に if-else if-else
の階層が深くなると,次第に判読が難しくなるため,
以下のようにタブを使って字下げ整形をする.
タブによる字下げは,半角スペース4文字分,または2文字分の場合が多い.
(VSCodeなどのプログラミング用のエディタでは,字下げの範囲を視覚的に表示できるものがあるので,便利である.)
if(式) {
文; /* 4文字インデント */
if(式) {
文; /* 8文字インデント */
if(式) {
文; /* 12文字インデント */
}
} /* 2番目の if の終わり */
} /* 最初の if の終わり */
if-else
をいくつも並べれば,任意の多分岐を表現できるが,コードが非常に見辛くなることが多い.
そこで,整数値の場合分けに限って,switch-case
文を用いることで多分岐をすっきり(?)と記述できる.
switch(変数) // この変数に置けるのは,int, short, char型など,整数型のみ.
{
case 定数1: // 変数が 定数1 なら,ここから次のbreakまで実行される.
文;
break;
case 定数2: // 変数が 定数2 なら,ここから次のbreakまで実行される.
文;
break;
case ...: // caseは何個でも入れることができる.
...
default: // 上の条件にどれも当てはまらない場合,ここが実行される.
文;
}
switch文の注意
case
に当てはまるものが何もなければ,何も実行されない.switch
はネスト(入れ子)することもできる.break
はなくてもエラーにはならない.続く行に処理が続行する.default
は無くてもよい.switch
で記述できるものは,すべてif-else
で書き換え可能.if-else
はどのような変数・条件式でも使えるのに対して,switch
で分岐できるのは整数のみ.switch
で複数のcase
にまたがる処理を書く
switch
文では,case
文は単なる処理のジャンプ先を示すラベルであるので,この性質を利用して,複数のcase
に同じ処理を簡単に対応させることができる.
以下の例では,変数 x が 1,3,5の場合に文1を,x=2,4,6の場合に文2を実行する.
#include <stdio.h>
int main(void)
{
int n;
printf("Saikoro(1 to 6) = ?");
scanf("%d", &n);
switch(n) {
case 1:
case 3:
case 5:
printf("%d is ODD.\n", n); // n = 1,3,5の時に実行される
break;
case 2:
case 4:
case 6:
printf("%d is EVEN.\n", n); // n = 2,4,6の時に実行される
break;
default:
printf("%d is out of range.\n", n); // 範囲外の数値が入力された場合
}
return 0;
}
switch-case
文の動作を確認してみよう.
while()
やfor
などのループ処理の中に,別のループ処理を置くことができ,ネストされたループ,またはループの入れ子と呼び,行列計算や画像処理など多次元の計算処理において非常によく使われる.
一般的には,ループ処理それぞれに対してカウンタ変数を別々に用意し(例えば,i, j, k...
),それぞれのfor
ループでインクリメントする.
二重ループ処理を用いて画面に三角形状の文字を出力.
#include <stdio.h>
int main(void)
{
int i, j; // 二重ループなので,カウンタ変数を2つ用意
for(j=0; j<10; j++) {
for(i=0; i<j+1; i++) { // 反復回数は定数でなくても良い.
printf("@");
} // 変数 i のループの終了
printf("\n");
} // 変数 j のループの終了
return 0;
}
もうひとつの例,2変数の和の計算処理を行いたい場合
#include <stdio.h>
int main(void)
{
for(int j=1; j<=3; j++) { // j は外側のloop.ここで変数を宣言しても良い.
for(int i=1; i<=3; i++) { // i は内側のloop.ここで変数を宣言しても良い.
printf("%2d + %2d = %2d ", i, j, i + j);
}
printf("\n"); // 1行ごとに改行
}
return 0;
}
いずれの場合も,カウンタ変数の取り扱いに注意すること.
つまり,どのカウンタ変数がどの(内側か外側)のループのカウントをしているかを間違えないこと.
上記のループのネストを使って,i , j の範囲を変更してみよ.
例:
画面表示が崩れる場合は,ターミナルのウインドウを大きくすると良い.
break
文は大変便利なもので,ループの反復処理中に,いつでもループから脱出できる.
以下は,while(1)
の部分が無限ループに見えるが,i
が11になるとif
文が真となりbreak
でループを終了するため,printf
文は10回,実行される.
プログラム例.
画面に数値がいくつまで表示されるでしょうか...
#include <stdio.h>
int main(void)
{
int i=0;
while(1) { // 無限ループに見えるが...
printf(" i = %d \n", i );
if(i > 10) {
printf("i = %d, break!\n" , i);
break; // i が 10より大きくなると,ループ終了...
}
i++;
}
// breakによりループを抜けだし,ここ以降の処理続行.
printf("End.\n");
return 0;
}
ループを抜け出したくはないが,ある条件ではその回のそれ以降の処理をスキップしたいこともある.
この様なとき continue
文を用いる.
例えば以下の例では,1から10までのうち,偶数のみ表示される.
プログラム例.
画面にどのような値が表示されるでしょうか...
#include <stdio.h>
int main(void)
{
int i;
for(i=1; i<=10; i++) { // i は 1 から 10 まで増加
if(i % 2 == 1) { // iが奇数だったら...
continue; // 次のループ処理に進む.
}
printf("i = %d \n", i); // ここに到達すると数値を表示
}
printf("End.\n");
return 0;
}
上記のソースコードを実行し,break, continue
の動作を確認しよう.
(注:break, continue
は,ループ処理の中に書かないとエラーになる!)
C言語では,文と呼ばれる「指定したソースコードの位置に無条件でジャンプする」機能を持つ構文も備えている.
しかし,goto
文を用いるとプログラム処理の流れが破壊され,どのような処理が行われているのか理解し難いプログラムになる.
また,goto
文を用いなければ実現不可能な処理は存在しないことがわかっている.
従って,ここでは goto
文の構文を解説することはしないので,興味があれば各自で調べてみよう.
goto
文は百害あって一利なしであり,使用しない方が良い.
条件分岐や繰り返し文には必ず条件式が登場するが,この条件式には,関係演算子だけではなく,あらゆる値を置くことができる.
その理由は,関係演算子は「演算子」であるため,演算結果は「値」となるからであるが,ここであらためて条件式の「値」について確認してみよう.
C言語では,falseの値は 0 である,と定義されている.
逆に,trueの値は,非ゼロ値( = ゼロではない値)となる.(計算機は数値しか認識しないことを思い出すと良い)
次の例題で,論理式の具体的な値を確かめてみよう.
#include <stdio.h>
int main(void)
{
printf("3>2 = %d \n", 3>2);
printf("3<2 = %d \n", 3<2);
return 0;
}
一般に,関係演算子による演算結果は,真の場合,非ゼロ値(多くの場合は1),偽の場合はゼロとなる.
上記のプログラムを実行すると,とある処理系では,
関係演算子の演算結果: 3>2 = 1 関係演算子の演算結果: 3<2 = 0
と表示される. 処理系によっては異なる結果が出る場合もある. 自分のPC環境で試してみよう.
試験の成績から平均点を計算するプログラムを作りたい.
ユーザーにキーボードから整数(0-100の点数とする)を繰り返し入力させ,-1 が入力されたら入力処理を終了,
それまで入力した(最後の -1 以外の)数値の個数と平均値を表示するプログラムを作成せよ.
入力間違いには,エッラーメッセージを表示する.もちろん点数には加算しないこと.
ヒント:
"%.1f"
を使用.実行例: Input:10 Input:20 Input:300 Error! Input:30 Input:40 Input:-1 ---- Data input done. ---- count = 4 average = 25.0
1∼1000までの値について,
という動作をするプログラムを作成せよ.
if() ∼ else if() ∼ else
文を使う.判定の順序に注意.実行例: [4] [5] [8] [10] [12] [15] [16] (20) [24] ...
手計算で倍数の計算するのではなく,プログラムに計算処理をさせること.
ダメな例:
#include <stdio.h>
int main(void)
{
printf("[4] [5] [8] [10] [12] [15] [16] (20) [24] ... \n");
return 0;
}
for
文の2重ループを使用して,以下に示すような縦横それぞれ1-3の数値の積を表示するプログラムを作りなさい.
ヒント:行,列の添え字の扱いに注意.
実行例:
1*1=1 1*2=2 1*3=3 2*1=2 2*2=4 2*3=6 3*1=3 3*2=6 3*3=9
定期預金や投資信託などにおいて,複利計算により資産額が当初の 2 倍を超えるのは,何年後になるかを計算するプログラムを作成しなさい.
例えば,初期の預入金額(元金,がんきん)は10,000円とする.
複利計算とは,前年度までの(元金+利息)に対して,さらに利息が付く計算方法である.
実際の金利水準などは,金融機関のWebなどで調べてみよう.
金利(年率)をキーボードから実数で入力すること.(例えば,3% のときは 0.03 を入力)
計算は実数で行うこと.表示は整数でも少数でも良い.
ヒント:1年経過するごとに,金額が (1.0 + 金利) 倍となる指数関数である
この計算を,ループ処理を用いて,n 年後に「もし金額が元の 2 倍を超えたら break;
」のような処理を行う.
「何回繰り返したか=何年経ったか」を表示する.
プログラムでは,無限ループと,break
文を使用すること.
実行例.(出力された数値が正しいとは限らない.) r=? 0.03 (←キーボード入力) 0 year : 10000 yen 1 year : 10300 yen 2 year : 10609 yen . . . ?? year : 20100 yen Your assets will double in ?? years.
// コード例
#include <stdio.h>
int main(void)
{
float r; // 金利
printf("r=?\n");
scanf(???);
float A = 10000; // 当初の資産額.
float An = A; // n年後の資産額.これをループで計算
int n = 0; // 経過年数.0で初期化.
// 初期の金額を表示
printf("0 year: ?? yen.\n");
while(1) { // 無限loop
An = ...
if(...)
break;
}
printf("Your assets will double in ?? years.\n");
return 0;
}