(17.1) if ... else を使った自明なプログラム。
#include <stdio.h> main() { int x; printf("整数を入力してください.\n"); scanf("%d", &x); if (x == 0) { printf("その数は零です.\n"); } else if (x > 0) { printf("その数は正です.\n"); } else { printf("その数は負です.\n"); } }
(17.2) if の行以降については次を見よ。
if (条件1) { 文1 } else if (条件2) { 文2 } else if (条件3) { 文3 } else { 文4 } |
|
(17.3) if が一つしかなく、かつ、else がないとき。
if (条件1) { 文1 } |
|
(17.4) K&R2 では §3.2, §3.3 を参照。 そこでは省略可能な中カッコを省略しているが、 私のやり方のように全てつけておくほうが間違いが少ない。 インデントについても上の例でしっかり覚えよ。 中カッコの中の文はタブ一つ分だけ余計に字下げする。
(17.5) if (...) の中で使える記号は、数学上の記号と少々異なる。
数学上の記号 | < | > | ≦ | ≧ | = | ≠ |
---|---|---|---|---|---|---|
C言語での記号 | < | > | <= | >= | == | != |
(17.6) 「==」(等号二つ)には特に注意すること。 「=」は代入の記号だったので比較の意味では使えない。 「もしも x が 0 に等しかったら」は 「if (x == 0)」であって、「if (x = 0)」ではない。 後者も文法的には正しいのでコンパイラはメッセージを出さないが、 別の意味になってしまう。
(17.7) 他のプログラム言語では「=<」「=>」 と書いても「≦」「≧」の意味になることがあるが、 C言語はそうではない。
(17.8) 「if (a+b < c+d)」のように if の小カッコ内で計算をしてから比較することもできる。
(17.9) K&R2 では §2.6, §A7.9, §A7.10 を参照のこと。
(17.10) 「かつ」や「または」は次のように書く。 「|」はシフトしながら¥のキーを打つ。 K&R2 では §2.6 を見よ。
if (x > 0 && x < 8) { /* 「かつ」 */ ........ } if (x < 0 || x > 2) { /* 「または」 */ ........ }
注意: 「0 < x < 8」はC言語でも意味のある式だが、 数学とは異なる意味になるので、使うな。
(17.11) 「かつ」も「または」も同じ文字を二つ打つことに注意。 まちがって「&」「|」をひとつだけ書いた場合、 それらにも意味があるのでコンパイラはエラーメッセージを出さない (かもしれない)。
(17.12) 練習問題その1.(17.1) のプログラムで、 else if の行を次のように変え、 実行時に 0 を入力したら、出力はどうなるか?
} else if (x >= 0) {
(17.13) 練習問題その2.「1 から 3 までの整数のうち、好きなものを入力してください」 と出力し、ユーザが入力した数に応じて、 「1 を選んだあなたは**な人です」というような文章を出力する、 “占い”プログラムを作れ。 「1 リンゴ、2 ミカン、3 バナナ のうち、好きな果物の番号を入力してください」 などとしてもよい。 1, 2, 3 以外の数を入力した場合も、何かメッセージを出すとよい。
(18.1) C言語でくり返しを実行させる方法には三種類あるが、 ここでは for だけを説明し、 while と do-while はとりあげない。 K&R2 では §1.2, §1.3, §3.5 を見よ。 do-while は §3.6 にある。
(18.2) for を使った自明なプログラム。
#include <stdio.h> main() { int i; for (i = 0; i <= 16; i++) { printf("%d の二乗は %d です.\n", i, i*i); } }
(18.3) for の働きをきちんと説明すれば、以下のとおり。
for (文1; 式2; 文3) { ... } |
|
※ 中カッコの中(ループ本体)が一度も実行されないこともありえる。
おおまかに理解するなら、 「文1から始めて、式2である限り、毎回文3としながら、 くり返す」となる。 上の例では 「i イコール 0 から始めて、i が 16 以下である限り、 毎回 i を 1 ずつ増しながらくり返す」となる。
(18.4) i++ は「変数 i の値を 1 だけ増せ」、 i-- は「変数 i の値を 1 だけ減らせ」の意味。
(18.5) 上のプログラムについて、for 文のみ説明する。 まず初期設定 i = 1 を実行。 次に継続条件 i <= 16 をチェックする。 ここでは 1 ≦ 16 だから成り立っている。 よって中カッコの中を実行。 再初期化 i++ が実行されて i の値は 2 に変わる。 そして継続条件 i <= 16 のチェックに戻る。 また成り立っているので中カッコの中を実行。 再初期化 i++ が実行されて i の値は 3 に変わる。 これをくり返してゆくと、 i は 16 まで、1 ずつ増されてゆく。 16 のときも中カッコの中は実行されるが、 次に再初期化 i++ を行なうと i の値は 17 になる。 すると今度は継続条件 i <= 16 が成り立たないので、 中カッコはとばして次へ進む。よって、プログラムは終わる。
(18.6) 式2の書き方は、 if の後ろの小カッコの中の書き方と全く同じ。
(18.7) 上限の 16 に深い意味はない。 いろいろ変えて動かしたい場合は、次のように書く。
#include <stdio.h> #define N 16 main() { int i; for (i = 0; i <= N; i++) { printf("%d の二乗は %d です.\n", i, i*i); } }
#define N 16 は、 「プログラム中では N は定数 16 として扱え」の意味。 この 16 を書き換えれば、上限が変わる。 書き換えるべき箇所がプログラムの先頭付近にくるので、便利である。
(18.8) 上のプログラムで「#define N」の右に書く数をだんだん大きくしてゆくと、 やがて int 型の限界を越えて答えがおかしくなる。 その場合でもエラーメッセージなどは一切出ない。試せ。
(18.9) N の値を大きくして実行すると、 「これは時間がかかりすぎだ、失敗!」と思うときがあろう。 プログラムの実行を中止してプロンプトに戻りたいときは Ctrl+C を押す。 途中でいったんとめて様子をみるときは Ctrl+S を押す。 任意のキーで再開。 (Ctrl+Q でなければ再開しないこともある。)
(18.10) 練習問題その1:(18.7) のプログラムを改変し、 i の値が N から 0 まで、1 ずつ減りながら出力されるようにせよ。
(18.10) 練習問題その2:(18.7) のプログラムを改変し、 i の値が 2 ずつ増えるようにせよ。 (文3は「i = i+2」となる。 「いまの i の値に 2 を加えたものを i に代入せよ」 の意味。)
(19.1) 変数名には、a, b, x, y のような一文字はもちろん、sum, total のように二文字以上からなる文字列も使える。 英語の単語でなくてももちろん可。 x11 のように数字がはいっても構わないが、 数字で始まる 1x などは不可。 (使おうとしたらどうなるか?)
(19.2) x1, x2, x3 とあったら人間は関連のある変数だろうと想像するが、 コンパイラにとっては全く無関係な、ただの三つの変数である。
(19.3) 「配列」(昔の書き方では「排列」)とは、 0 からある自然数までの整数で添え字づけられた有限個の変数 x[0], x[1], x[2], ..., x[N-1] を一斉に定義し、一括して扱うものである。 K&R2 では §1.6 を参照。
(19.4) 配列を使った自明なプログラム。
#include <stdio.h> int a[10]; /* 配列の宣言。これで a[0], ..., a[9] が使える */ main() { int i; for (i = 0; i < 10; i++) { /* 配列の要素に代入 */ a[i] = i * i; } for (i = 0; i < 10; i++) { /* 配列の要素を印字(=出力) */ printf("a[%d] は %d です.\n", i, a[i]); } }
(19.5) 「配列の宣言。...」の行。こう書くと、 a[0] から始まる 10 個の int 型変数が使える。 すなわち、a[0], a[1], a[2], ..., a[9] が使える。 a[10] は使えない。うっかり使わないよう、注意。
(19.6) 「配列の宣言は main() の中カッコの外で行ない、 ほかの変数の宣言は main() の中カッコの中で行なう」 という規則があるわけではないが、 ある事情により、この授業ではこうすることが多い。 だからこれが規則だと思っておけばよい。 正確なところは K&R2 §1.10, §4.3 参照。
(19.7) 上のプログラムの配列の大きさを、(18.7) で説明した #define を利用して書いたもの。 複数回あらわれる N を、一斉に変えることができて便利である。 実際に変えて実行してみよ。
#include <stdio.h> #define N 10 int a[N]; /* 配列の宣言。これで a[0], ..., a[N-1] が使える */ main() { int i; for (i = 0; i < N; i++) { /* 配列の要素に代入 */ a[i] = i * i; } for (i = 0; i < N; i++) { /* 配列の要素を印字(=出力) */ printf("a[%d] は %d です.\n", i, a[i]); } }
(19.8) 練習問題:(19.7) のプログラムを改変し、 a[i] に i*i を代入したあと、 隣あった項どうしの差 (a[1] - a[0], a[2] - a[1], ..., a[N-1] - a[N-2]) を出力するプログラムを書け。 出力時に、添え字が範囲外の値にならないよう注意せよ。
(20.1) きょう学んだ三つを利用した、少しは意味のあるプログラムの例である。 2 から N*N までの自然数について、 素数の場合は「... は素数です」と、 そうでない場合は「... の最小の素因数は ... です」と出力する。
#include <stdio.h> #define N 10 int a[N*N+1]; /* これで a[0] から a[N*N] までが使える */ main() { int i, n; for (n = N*N; n > 1; n--) { for (i = 1; i*n <= N*N; i++) { a[i*n] = n; } } for (n = 2; n <= N*N; n++) { /* 以下、結果の出力 */ if (a[n] == n) { printf("%d は素数です.\n", n); } else { printf("%d の最小の素因数は %d です.\n", n, a[n]); } } }
(20.2) a[N*N] まで使える配列を用意し、 添え字が n の倍数である要素に n を代入するという操作を、 n が大きいほうから小さいほうへ、N*N から 2 まで、 順に行なう。 それが終わると、 a[n] には n の最小の素因数がはいっている、 という原理に基いている。
(20.3) N の値を 10000 にすると、このプログラムは、 出力が始まるまで1分少しかかる。 その間は、最初の二重ループを回って計算をしているのである。
(20.4) 中カッコが入れ子になっている例が出てきた。 その場合のインデントの(一つの)やり方は以下の通りである。
(20.5) これらの規則はすぐに慣れることができるし、 これらを守ってさえいれば、「ある中カッコとペアになる中カッコをさがす」 ことや「ある文を囲む最小の中カッコをさがす」ことはきわめて容易である。 前にも言ったが、 自分流のインデントをすでに編み出している人はそれで構わない。 そうでない人はまずはこれを覚えよ。 また、プログラムを書き上げてから最後にインデントを整えるのではなく、 きちんとインデントしながら書く習慣をつけるとよい。 そのほうが、すでに書いた部分をよりよく理解できるので、 より整理された頭でプログラムを書き進めることができる。
(20.6) 練習問題その1:上のプログラムを、素数のみを出力するよう改変してみよ。
(20.7) 練習問題その2:上のプログラムを、 2000000 以上 2001000 未満の素数のみを出力するよう改変してみよ。 このとき、N はいくつにすればよいか?
#include <stdio.h> main() { double a, b; printf("数を入力してください.\n"); scanf("%lf", &a); printf("もう一つ数を入力してください.\n"); scanf("%lf", &b); if (b == 0) { /* if による分岐 */ printf("0 では割れません.\n"); } else { printf("%f 割る %f は %f です.\n", a, b, a/b); } }
小数点以下がある数を扱うには、 「double 型」を用いるのが基本である。 これは、浮動小数点数(i.e. 小数点以下がある数、 しかも小数点以下の桁数が自動的にいろいろ変わる) を格納する型のうちの代表的なものである。 型が違うと、一般に入出力の際の書き方も違う。 double 型の入力の際は %lf(l はエル)、 出力の際は %f と書く。
%f の代わりに %20f と書くと「幅 20 ケタで」の意味。 %.30f と書くと「小数点以下 30 ケタで」の意味。 %50.20f は両者の組み合わせ。
int 型と double 型の両方を使うプログラムでは、 次のようにして変数を宣言する。
int i, j, k; double x, y;
数学に出てくる関数を特に「数学関数」と呼ぶ。 数学関数を用いているプログラムは、冒頭に #include <stdio.h> だけでなく、 #include <math.h> とも書く。 また、このCコンパイラの約束事として、コンパイル時に オプション「-lm」(マイナス、エル、エム)をつけて 「gcc -lm filename.c」のようにしてコンパイルする。 つけないとどうなるか、試してみるとよい。
#include <stdio.h> #include <math.h> main() { double x; x = 2; printf("%f\n", sqrt(x)); }
sqrt() は平方根を計算する関数である。 sin(), cos(), tan() が三角関数。 asin() などが逆三角関数。 exp() は指数関数、 log() は自然対数、log10() が 10 を底とした対数。
これで、二次方程式を解くプログラム、 数列の極限を計算するプログラムなどが書ける。