次のプログラムを見て欲しい。
/* Solve Quadratic Equation */
#include <stdio.h>
#include <math.h>
int main(void) {
double a, b, c, d;
printf("Solve Quadratic Equation ax^2+bx+c=0\n");
printf("a=");
scanf("%lf", &a);
printf("b=");
scanf("%lf", &b);
printf("c=");
scanf("%lf", &c);
d = b * b - 4 * a * c;
if (d > 0) {
printf("Solution1=");
printf("%f\n", (-b+sqrt(d))/(2*a));
printf("Solution2=");
printf("%f\n", (-b-sqrt(d))/(2*a));
} else {
if (d == 0) {
printf("Solution=");
printf("%f\n", -b/(2*a));
} else {
printf("No Solution.\n");
}
}
return 0;
}
|
これは、2次方程式 ax2+bx+c=0の係数をキーボードから 入力し、解を計算するプログラムである。
if文は、
if (条件式) 文1 |
または、
if (条件式) 文1 else 文2 |
という形をしている。 前者の場合、条件式が成立すれば「文1」を実行し、成立しなければ何もしない。 後者の場合、条件式が成立すれば「文1」を実行し、成立しなければ「文2」を 実行する。
「文」とは「int a;」などの宣言文や「z = x + y;」などの 式文を指すが、
{
文
文
文
....
}
|
のように「文を複数並べて{}で括ったもの」もまた「文」である(「複文」という)。 従って、if文の中の文に複文を使えば、
if (条件式) {
文
文
...
}
|
または、
if (条件式) {
文
文
...
} else {
文
文
...
}
|
のような形になる。普通if文の中に書く実行部分は 複数の文からなることが多いので、むしろこちらがifの形だと 覚えてしまった方が良い。確かに文が1つのときは
if (a == 0) x = 1; else x = 2; |
のように書くことは出来るが、構造が分かりにくくなるのでお奨めしない。 たとえ文が一つでも、
if (a == 0) {
x = 1;
} else {
x = 2;
}
|
と書いた方がよい。
上の2次方程式の例題では、elseの中に更にif文が 入っている。このようにif文は入れ子にすることが出来る。
| a == b | aとbが等しければ1 |
| a != b | aとbが等しくなければ1 |
| a > b | aがbより大きければ1 |
| a < b | aがbより小さければ1 |
| a >= b | aがbより大きいまたは等しければ1 |
| a <= b | aがbより小さいまたは等しければ1 |
等号が「=」でなく「==」であることに注意。 ここを間違えることは多い。
次に、これらを組み合わせるための、論理演算子をいくつか示す。
| a || b | aとbのうち少なくとも片方が真(0でない)なら1 |
| a && b | aとbのうち両方が真(0でない)なら1 |
| ! a | aが真(0でない)なら0、偽(0)なら1 |
これを使うと、
if ((1 <= a) && (a <= 9)) {
文
}
|
のような複雑な条件を書くことが出来る。
#include <stdio.h>
int main(void) {
int n, a;
scanf("%d", &n);
a = 1;
while (a < n) {
a *= 2;
}
printf("%d\n", a);
return 0;
}
|
while文は、文法的には
while (条件式) 文 |
だが、ifと同様
while (条件式) {
文
文
...
}
|
であると覚えてしまってよい。条件式が成立するか調べ、成立していれば 文を実行、これを繰り返す。条件式が成立しなければ終了し次へ行く。 条件式が成立している限りは繰り返すため、条件式がいつかは成立しなく なるようにプログラムが書かれていなければ、無限ループに陥る危険がある。 無限ループになったら、Ctrl-C (Ctrlキーを押しながらcを押す) で終了できる。
また、「break;」によって強制的にループを脱出し 次へいくことが出来る。 次の例は、自然数を入力するとその平方根を表示する、という 作業を繰り返し、もし負の数が入力されたら終了するという プログラムである。
#include <stdio.h>
#include <math.h>
int main(void) {
double x;
while (1) {
scanf("%lf", &x);
if (x < 0) break;
printf("%f\n", sqrt(x));
}
return 0;
}
|
ここで、「while (1)」は常に条件が成立、すなわち無限ループを 表す。脱出はbreakによって行われる。
このように、ループを書くのにとりあえず「while (1)」で 無限ループにしておき、ifとbreakで好きなときに 脱出するという書き方は「私は」書き易いと思う。なお、do-while文というものもある。こちらは、 文法的には
do 文 while (条件式); |
だが、ifと同様
do {
文
文
...
} while (条件式);
|
であると覚えてしまってよい。普通のwhileとの違いは、 文を実行した後に条件式を調べることである。 無限ループとbreakで書くなら、普通のwhile文は、
while (1) {
if (! 条件式) break;
文
文
...
}
|
であるのに対して、do-while文は
while (1) {
文
文
...
if (! 条件式) break;
}
|
であり、脱出の位置が違う。これを見ると、普通のwhile文は 中身が一度も実行されないことが有り得るのに対して、do-while文は 一度は必ず実行されることが分かる。
i = 1;
while (i <= 10) {
printf("%d\n", i);
i++;
}
|
のようになるであろう。このように、
for (i=1; i<=10; i++) {
printf("%d\n", i);
}
|
のように書け、変数を1から10まで増やすということが明確になっている。
for (i=10; i>=1; i--) {
printf("%d\n", i);
}
|
のように書くと、10から1まで値を減らしながら表示する。また、
for (i=0; i<=10; i+=2) {
printf("%d\n", i);
}
|
のように書くと0,2,4,6,8,10と表示する。
for文は、文法的には
for (式1; 式2; 式3) 文 |
という形であり、これは
式1;
while (式2) {
文
式3;
}
|
と等価である。
for文は、数値を増やしながらという用途だけでなく、 ループの回る回数を指定するという意味での使い方も多い。例えば、
for (i=0; i<10; i++) {
printf("Hello World\n");
}
|
で「Hello World」を10回表示する。
#include <stdio.h>
#include <math.h>
int main(void) {
double x;
while (1) {
scanf("%lf", &x);
if (x < 0) continue;
printf("%f\n", sqrt(x));
}
return 0;
}
|
これは、入力された数に対しその平方根を計算し表示するものである。 但し入力が負の数なら何もしない。 このプログラムは永久に終了しないため終わらすにはCtrl-Cを入力する。 xが負ならばその次の行は飛ばされ、次のループの実行へと 移っている。
ループ中のcontinueは、例えば
for (i=0; i<10; i++) {
文1
文2
if (条件) continue;
文3
文4
}
|
ならば、
for (i=0; i<10; i++) {
文1
文2
if (! 条件) {
文3
文4
}
}
|
と同じなので、breakと違って必須の機能というわけではない。
| 90~100 | A+ |
| 80~89 | A |
| 70~79 | B |
| 60~69 | C |
| ~59 | F |
に従って評価を出力するプログラムである。
#include <stdio.h>
int main(void) {
int x;
scanf("%d", &x);
if (x >= 90) {
printf("A+");
} else {
if (x >= 80) {
printf("A");
} else {
if (x >= 70) {
printf("B");
} else {
if (x >= 60) {
printf("C");
} else {
printf("F");
}
}
}
}
printf("\n");
return 0;
}
|
このプログラムは、90点以上ならA+、そうでなくて80点以上ならA、 というような素直なプログラムであるが、極端にif文の ネストが深くなって見辛くなってしまっている。 確かにプログラムの構造を明らかにするのにちゃんと字下げを 行うことは重要であるが、このような連鎖したifの 場合は、特別に次のように書くことが多い。
#include <stdio.h>
int main(void) {
int x;
scanf("%d", &x);
if (x >= 90) {
printf("A+");
} else if (x >= 80) {
printf("A");
} else if (x >= 70) {
printf("B");
} else if (x >= 60) {
printf("C");
} else {
printf("F");
}
printf("\n");
return 0;
}
|
これはelseに本来あるべき{}を省略していて普通ならば やらない書き方だが、この場合に限っては、
if --- else if --- else if --- else if --- else 文とでも言うべき文があると思って理解するのがよい。
switch (式) {
case 値1:
文
break;
case 値2:
文
break;
case 値3:
文
break;
default:
文
}
|
のような形で、式の値を計算し、その対応する値のラベルへジャンプするという 動作をする。default:は、対応する値が無かった場合に飛ぶ 場所で、無くてもよい。default:が無い場合、対応する値が 無ければswitch文は何もしない。
式の値は、int型でなくてはならない。
文の後にあるbreak;は、switchから脱出するためのもので、 これがないとそのまま次の値に対する実行文の部分まで突入してしまう。
前の成績を出力するプログラムをswitchで書くと、
#include <stdio.h>
int main(void) {
int x;
scanf("%d", &x);
switch (x / 10) {
case 10:
case 9:
printf("A+");
break;
case 8:
printf("A");
break;
case 7:
printf("B");
break;
case 6:
printf("C");
break;
default:
printf("F");
}
printf("\n");
return 0;
}
|
のように書ける。「case 10:」の部分では、あえてbreak; を書かずに次へ走り抜けることを利用している。
while (1) {
文
文
...
}
|
は、
label1:
文
文
...
goto label1;
|
と書くことも出来る。
便利なものであるが、乱用するとプログラムの流れが分かりづらくなるので、 よほどのことが無い限り使ってはならないとされている。