2006 年度「計算機基礎論3B」 2006-10-27

§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 sin.c」としてコンパイルすること */
#include <stdio.h>
#include <math.h>   /* sin */

#define PI 3.14159265

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

(17.1) 数学に出てくる関数を特に「数学関数」と呼ぶ。 数学関数を用いているプログラムは、 オプション「-lm」(マイナス、エル、エム)をつけて 「gcc -lm sin.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, sum;

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

    sum = 0;

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

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

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

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

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

§19 練習問題

(19.1) プログラムの実行を中止してプロンプトに戻りたいときは Ctrl+C を押す。 途中でいったんとめて様子をみるときは Ctrl+S を押す。 再開は Ctrl+Q である。 (センターの linux は任意のキーで再開できるようだが。)

(19.2)

#include <stdio.h>

main() {
    int i;

    for (i = 0; i < 10; i++) {
        printf("i の値は %d です.\n", i);
    }
}

まずこのプログラムを理解しよう。 次に、これを改造してみよう。

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

#include <stdio.h>

main() {
    double x;

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


岩瀬順一