2007 年度「計算機基礎論3B」 2007-10-26

§14 ごく簡単な計算

    /* たし算  1990-04-28, written by Iwase */

#include <stdio.h>

main() {
    int a, b, s;                /* 変数の宣言 */

    a = 2;                      /* 代入 */
    b = 3;
    s = a + b;

    printf("%d たす %d は %d です.\n", a, b, s);
}

(14.1) 1行目。 「/*」と「*/」とで囲まれた部分はコメントである。 何を書いてもコンパイルしてできる実行ファイルには関係ないので、 自由にメモを書くことができる。 ここでは、何をするプログラムか、いつ誰が作ったかを書いている。 その下のコメントは以下で説明する際の便宜のためにつけたものである。 これらは諸君は打ち込まなくても構わない。

(14.2) 「変数の宣言」の行。 変数を使う場合、前もって「宣言」しておく必要がある。 ここでは「変数 a, b, sint 型」と宣言している。 変数名には小文字を使うのが普通である。 「int 型」は整数のみを格納できる「整数型」の一つで、 この名前は整数 integer に由来している。 私は「イントがた」と読んでいる。 変数の宣言はこの位置(i.e. main() が始まってすぐのところ) で行なわなければならない。 その次には空行をおくと見やすくなる。 なお、中カッコの中の行は(空行以外)すべて字下げする。

(14.3) 「代入」の行。 「a = 2」は、「a は 2 に等しい」という平叙文ではなく、 「a に 2 を代入せよ」という命令文である。 その下の二行も同様。 プログラムは特に指定しなければ上から下へと実行されるので、 s = a + b; を実行したあとの s の値は 5 になる。 なお、 「=」は Shift しながら「-」のキー、 「+」は Shift しながらセミコロンのキーを打つ。 「a = 2;」の 「a」「=」「2」の間にはスペースを置いても置かなくてもよい。

(14.4) printf 文の行。 記号「%d」は、順に後ろの変数の値に置き換わって画面に出力される。 ここでは、 一つめの %da の値で、 二つめの %db の値で、 三つめの %ds の値で置き換わる。

    printf("%d たす %d は %d です.\n", a, b, s);
            --      --    --           -  -  -
            |       |     |            |  |  |
            +-------|-----|------------+  |  |
                    +-----|---------------+  |
                          +------------------+
よって、出力は「2 たす 3 は 5 です.」のあと改行、となる。 「%」は Shift しながら 5 のキーを打つ。 (句点「」の代わりに半角のピリオドを使っているのは単に趣味の問題である。)

(14.5)

(14.6) 練習: 0 で割り算をするとどうなるか試せ。具体的には、

(14.7) 練習: 出力の文の二重引用符の間の文字列からスペースを除去し、 「printf("%dたす%dは%dです.\n", a, b, s);」 としたら出力はどう変わるか。 「printf("%d%d%d\n", a, b, s);」としたらどうなるか。

§15 scanf() によるキーボードからの入力

#include <stdio.h>

main() {
    int a, b;

    printf("数を入れてください.\n");
    scanf("%d", &a);                        /* 数の入力 */
    printf("もう一つ数を入れてください.\n");
    scanf("%d", &b);
    printf("%d たす %d は %d です.\n", a, b, a+b);
}

(15.1) 「数の入力」の行。 ここでプログラムは止まってキーボードからの入力を待つ。 関数 scanf() はそのための関数である。 私は「スキャンエフ」と読んでいる。 変数名 a の前に & がついて &a となっていることに注意。 なぜ & が必要かは、いまは説明できない。 scanf() は K&R2 §7.4 でくわしく論じられているが、 いま読んでもむずかしいだろう。

(15.2) 三つめの printf()a+b の値を印字させていることにも注意。 (いままでの例では、変数の値を出力させていた。)

§16 double 型、if ... else による分岐

#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);
    }
}

(16.1) 新しく「double 型」が出てきた。 これは、浮動小数点数(i.e. 小数点以下がある数、 しかも小数点以下の桁数が自動的にいろいろ変わる) を格納する型のうちの代表的なものである。 型が違うと、一般に入出力の際の書き方も違う。 double 型の入力の際は %lfl はエル)、 出力の際は %f と書く。 これを間違うとめちゃめちゃな値が出力されたりする。

(16.2) この授業では int 型と double 型のみを用いる。 この例と前の例とでこれら二つの型の入出力がすべて現れているので、 わからなくなったらこれらを見るとよい。

(16.3) 「if による分岐」の行からの5行は次を参照。

    if (条件1) {           
        文1
    } else {
        文2
    }
  • 条件1が真ならば文1を実行し、文2は実行せずに先へ進む。
  • 条件1が偽ならば文1は実行せず、文2を実行して先へ進む。

(16.4) 長い形のとき。これをよく理解しておくこと。

    if (条件1) {
        文1
    } else if (条件2) {    
        文2
    } else if (条件3) {
        文3
    } else {
        文4
    }
  • 条件1が真ならば文1だけを実行する。
  • 条件1が偽で条件2が真なら文2だけを実行する。
  • 条件1も条件2も偽で条件3が真ならば文3だけを実行する。
  • 条件1も条件2も条件3も偽なら文4だけを実行する。
   (文のうち一つだけが実行されることに注意。)

(16.5) else のないとき。

    if (条件1) {           
        文1
    }
  • 条件1が真ならば文1を実行する。
  • 条件1が偽ならば何も行なわない。

(16.6) K&R2 では §3.2, §3.3 を参照。 そこでは省略可能な中カッコを省略しているが、 私のやり方のように全てつけておくほうが間違いが少ない。 インデントについても上の例でしっかり覚えること。 中カッコの中の文はタブ一つ分だけ余計に字下げする。

(16.7) if (...) の中で使える記号は、数学上の記号と少々異なる。

数学上の記号
C言語での記号 <><= >===!=

(16.8) 「==」(等号二つ)には特に注意すること。 「=」は代入の記号だったので比較の意味では使えない。 「もしも b が 0 に等しかったら」は 「if (b == 0)」であって、「if (b = 0)」ではない。 後者も文法的には正しいのでコンパイラはメッセージを出さないが、 別の意味になってしまう。

(16.9) 他のプログラム言語では「=<」「=>」 と書いても「≦」「≧」の意味になることがあるが、 C言語はそうではない。

(16.10) 「if (a+b < c+d)」のように if の小カッコ内で計算をすることもできる。

(16.11) K&R2 では §2.6, §A7.9, §A7.10 を参照のこと。


§17 数学関数

    /* 「gcc -lm filename.c」としてコンパイルすること */
#include <stdio.h>
#include <math.h>   /* sin */

#define PI 3.14159265

main() {
    printf("30 度の正弦は %f です.\n", sin(30*PI/180));
}

(17.1) 数学に出てくる関数を特に「数学関数」と呼ぶ。 数学関数を用いているプログラムは、 オプション「-lm」(マイナス、エル、エム)をつけて 「gcc -lm filename.c」のようにしてコンパイルする必要がある、 というのがこのCコンパイラの約束である。 つけないとどうなるか、あとで試してみるとよい。

(17.2) 「#define ...」の行は、 「以下で PI が出てきたら 3.14159265 で置き換えろ」 という意味である。 pi は円周率を表す π の英語名。 3.14... とあれば円周率だとたいていわかるが、 定数によっては意味がわかりにくいこともあろう。 そういう場合に、こうやって名前をつけるとわかりやすくなる。 また、何度も出てくる場合にはタイピング量の節約になるし、 打ち間違いが起こる率も減らせるだろう。 このような定数は大文字で書く習慣である。 これについては K&R2 の §1.4 を参照のこと。 なお、π をかけて 180 で割っているのは弧度法に直すため。 関数 sin() は、カッコの中に書く値の単位は radian という約束である。

(17.3) 数学関数を使うには、 3行目のように math.h の include が必要である。 「/* sin */」というコメントは、 関数 sin() を使うから math.h を include したんだぞ、という意味でつけてある。

(17.4) 平方根を計算する関数は sqrt() である。 ほかの数学関数については K&R2 の §7.8.6, §B4 を見よ。

(17.5) ここまでに習ったことを組み合わせると、 「二元の連立一次方程式を解く」「二次方程式を解く」 などのプログラムが作れるはずである。 興味のある人はどんどんやってみよう。 二次方程式は実係数の場合だけを扱えばよいが、 解が虚数の場合も答えを出すようにするとなおよい。


§18 for によるループ

(18.1) C言語でくり返しを実行させる方法には三種類あるが、 ここでは for だけを説明し、 whiledo-while はとりあげない。 K&R2 では §1.2, §1.3, §3.5 を見よ。 do-while は §3.6 にある。

(18.2)

    for (文1; 式2; 文3) {
        ...
    }
中カッコの中(ループ本体)が一度も実行されないこともありえる。

#include <stdio.h>  /* ユーザが数を入力すると,1 からその数までの和を出力 */

main() {
    int i, n, s;

    printf("いくつまで足しますか?\n");
    scanf("%d", &n);

    s = 0;

    for (i = 1; i <= n; i++) {
        s = s + i;
    }
    printf("1 から %d まで足すと %d です.\n", n, s);
}

(18.3) for 文のみ説明する。n には 10 が代入されているとしよう。 まず初期設定 i = 1 を実行。 次に継続条件 i <= n をチェックする。 ここでは 1 <= 10 だから成り立っている。 よって中カッコの中を実行。 s の値は 0 だったから i の値 1 を加えると 1 に変わる。 再初期化 i++ は「i を 1 だけ増やせ」の意味である。 これが実行されて i の値は 2 に変わる。 そして継続条件 i <= n のチェックに戻る。 また成り立っているので中カッコの中を実行。 s の値は 3 に変わる。 これをくり返してゆくと、 i は 10 まで、1 ずつ増えながら s に足されてゆく。 そのときの s の値が 1+2+…+10 である。 次に再初期化 i++ を行なうと i の値は 11 になる。 すると今度は継続条件 i <= n が成り立たないので、 中カッコはとばして次の printf() の行へ進む。

(18.4) 「i--」と書くと「i を 1 だけ減らせ」の意味になる。 K&R2 §2.8 参照。

(18.5) s = s + i; は等式としては正しくないが、 この「=」は代入の意味であるから問題ない。

(18.6) 上のプログラムで入力する数を 10, 100, 1000, 10000, ... のようにだんだん大きくしてゆくと、 どこかで int 型の限界を越えて答えがおかしくなる。 その場合でもエラーメッセージなどは一切出ない。 (やってみよう。)

§19 練習問題

(19.3) 次のプログラムは、 誤差の関係で、想像されるようには動かない。 (想像してから動かしてみよ。)
#include <stdio.h>

main() {
    double x;

    for (x = 0; x < 1; x = x + 0.1) {
        printf("x の値は %f です.\n", x);
    }
}


岩瀬順一