2015 年度「計算数学」 2015-10-09

§2.1 プログラムを書くとは

(2.1.1) プログラムを書くに当たっては, テキストエディタで「ソースファイル」を作成し, 「コンパイラ」でそれを「コンパイル」し,「実行ファイル」を作る (のが普通である)。

        +----------------+                                 +----------------+
        | ソースファイル |  ---------------------------->  |  実行ファイル  |
        +----------------+     コンパイラでコンパイル      +----------------+
    (テキストエディタで書く)                              (これを動かす)
         (読んでわかる)                               (機械にしかわからない)

(2.1.2) 「実行ファイル」はコンピュータが読んで実行するもので, 人間が読んでもまずわからない。 「ソースファイル」は文字だけが並んでいるテキストファイルで, 訓練をつめば読み書きできるようになる。 「コンパイル」はここでは「変換する」「翻訳する」ぐらいの意味。 ソースファイルを書く際に用いる言語はいろいろあるが, ここでは「C言語」(シーげんご)を用いる。 C言語で書かれたソースファイルを実行ファイルにコンパイルするコンパイラのことを 「Cコンパイラ」とも言う。 Cコンパイラの使い方は,この授業で扱う範囲では極めて簡単である。 よって, これから学ぶことの中心はC言語によるソースファイルの書き方である。

(2.1.3) 【注意】 コンピュータにおいて, バックスラッシュ「」と円マーク「」 は半角文字では同じものと思ってよい。 スラッシュ「」は別物である。向きが違う。混同するな。 右手で文字を書く人にとって書きやすいほうがスラッシュ, 逆向きがバックスラッシュと覚えるとよい。

§2.2 初めてのCプログラム

(2.2.1) 次の例はカーニハン/リッチー著・石田晴久訳 「プログラミング言語C第2版」(共立出版) (以下 K&R2 と略す)の §1.1 とほとんど同じである。 画面に「hello, world」と出力するもの。

#include <stdio.h>

main() {
    printf("hello, world\n");
}

(2.2.2) このプログラムについては, 「(いわゆる)ホームページからコピー & ペースト(以下「コピペ」という。)」 「キーボードから自分で打ち込む」 の両方で試せ。解説と注意は,(2.2.4) 以下に書いてある。 まずはコピペで試すとよいだろう。

(2.2.3) これを作成し,コンパイルし,実行する際の手順は次の通り。

  1. ホームに hello.c という名前の空のファイルを作る。
  2. それをダブルクリックして gedit で開く。
  3. プログラムを,コピペするか,自分で打ち込む。 その内容は上のとおり。保存するのを忘れずに。 (gedit は終了しないほうが,修正する際に便利である。)
  4. 端末に「cc hello.c」と打ち込んでコンパイルする。 何も出力されずにプロンプトが出れば成功である。 そのときは実行ファイル a.out ができている。
  5. 端末に「./a.out」と打ち込んで,プログラムを実行する。

失敗したときは 3 に戻ってやり直し。

(2.2.4) 1 行目。 きょうのところはこれは「決まり文句」と考えよう。 「#」は Shift しながら 3 のキーを打つ。 この記号は「シャープ」と呼んでおくが,正式名称は別にあったと思う。 include は英単語そのままだ。 この次にスペースを入れているが,入れなくてもよい。 stdio.h を囲む 「<」,「>」は不等号をカッコの代わりに使っているもの。 Shift しながら,それぞれ,カンマ,ピリオドのキーを打つ。

(2.2.5) 2 行目は空行である。見やすくするためにおいたもの。意味はない。

(2.2.6) 3 行目と 5 行目もCプログラムに必ずおくもの。 「main」の次にあるのは小カッコの開きと閉じ。 Shift しながら 8 と 9 のキーを打つ。 この次にスペースを入れているが,入れなくてもよい。 その次にあるのは中カッコの開き。 5 行目にあるのが中カッコの閉じ。 中カッコは,Shift しながら大カッコのキー(Enter のすぐ左にある)を打つ。 これらの中カッコは 4 行目を囲んでいる。つまり, 「main() { ... }」と同じである。 こう書かずに上のようにする理由はそのうちわかってくる。 また,この中カッコの書き方は K&R2 とやや異なる。 この授業では一貫して私の書き方で教えるので, すでに自分流のつけ方を編み出している人以外はとりあえずそれにならえ。

(2.2.7) よって,4 行目がこのプログラムのうちで最も意味のある部分ということになる。 この行は頭に空白がある。 この空白は Tab(タブ)キーを一度押すことでとる。 空白を置く理由はそのうちわかってくる。 このように行頭に空白を置くことを「字下げ」「インデント」という。 また,そこには tab という見えない文字があると考える。 (プログラムを(いわゆる)ホームページからコピペした場合には, インデントの空白は tab ではなくスペース四つになるが, コンパイル結果には影響しないので気にするな。)

(2.2.8) printf() は元々用意されている「関数」である。 「プリントエフ」と読むことが多いかと思う。 関数の原語は function で,ここでは本来の意味である「機能」に近い。 この関数の機能は後ろの小カッコの中の文字列を印字する(=画面に書く) ことである。 文字列は二重引用符「" "」 (Shift しながら 2 のキーを打つ)で囲む。 ここでは印字される文字列に「\n」が含まれている。 「\n」はこの二文字で「改行」を意味する。 すなわち,これを画面に書くとそこで改行する。 よって,「hello, world\n」を画面に書くということは, 「hello, world」を画面に書いて最後に改行する, ということになる。

(2.2.9) 4 行目の最後にはセミコロン「;」がついている。 C言語では文はセミコロンで終わることになっているので,忘れるな。 また,この「セミコロン」などの名称もこの機会にきちんと覚えよ。

(2.2.10) 練習: 出力される文字列を別のものに変えて, コンパイル,実行してみよ。

§2.3 ごく簡単な計算

(2.3.1) 以下では,ファイル名は適当に決めよ。 C言語のソースファイルは「.c」で終わる名前でなければならない。

(2.3.2) 次のプログラムは,まずはコピペして実行してみよ。 「2 たす 3 は 5 です.」と画面に書くだけのプログラムである。

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

    /* たし算  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);
}

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

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

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

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

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

(2.3.7)

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

§2.4 for によるループ

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

(2.4.2)

#include <stdio.h>      /* 0 から 99 までの整数の二乗を計算して出力 */
 
main() {
    int n;
 
    for (n = 0; n < 100; n++) {
        printf("%d の二乗は %d です.\n", n, n*n);
    }
}

(2.4.3)

#include <stdio.h>      /* 0 から 99 までの整数の足し算 */
 
main () {
    int n, sum;
 
    sum = 0;
    for (n = 0; n < 100; n++) {
        sum = sum + n;
        printf("0 から %d まで足すと %d です.\n", n, sum);
    }
}

(2.4.4) for の働きは以下のとおり。

    for (文1; 式2; 文3) {     
        ...
    }
  • 文1(初期設定)を実行。(最初に一度だけ。)
  • 式2(継続条件)が成り立つかどうかチェックする。
    • 式2が真ならば中カッコの中を実行。 その後,文3(再初期化)を実行し,式2のチェックにもどる。
    • 式2が偽ならば,中カッコはとばして次へ進む。

※ 中カッコの中(ループ本体)が一度も実行されないこともありえる。

おおまかに理解するなら, 「文1から始めて,式2である限り,毎回文3としながら, くり返す」となる。上の例では, 「n イコール 0 から始めて,n が 100 未満である限り, 毎回 n を 1 ずつ増しながらくり返す」となる。

(2.4.5) n++ は「変数 n の値を 1 だけ増せ」, n-- は「変数 n の値を 1 だけ減らせ」の意味。

(2.4.6) 上のプログラムについて, for 文だけをくわしく説明する。 まず初期設定 n = 0 を実行。 次に継続条件 n < 100 をチェックする。 ここでは 0 < 100 だから成り立っている。 よって中カッコの中を実行。 再初期化 n++ が実行されて n の値は 1 に変わる。 そして継続条件 n < 100 のチェックに戻る。 また成り立っているので中カッコの中を実行。 再初期化 n++ が実行されて n の値は 2 に変わる。 これをくり返してゆくと, n は 99 まで,1 ずつ増されてゆく。 99 のときも中カッコの中は実行されるが, 次に再初期化 n++ を行なうと n の値は 100 になる。 すると今度は継続条件 n < 100 が成り立たないので, 中カッコはとばして次へ進む。

(2.4.7) for の中かっこで囲まれた文は, tab 2つだけ字下げして(=右に寄せて)書く。 くわしくは §2.5 を参照。

(2.4.8) 上のプログラムの 100 を別の数に変えて試せ。

(2.4.9) 練習:(2.4.3) のプログラムをもとに, 1 から n までの自然数の積 --- n の階乗 --- を出力するプログラムを書いてみよ。 sum に代わる変数名としては prod(product の略)がよかろう。

§2.5 中カッコの入れ子

(2.5.1) 中カッコが入れ子になった場合のインデントの(一つの)やり方は, 以下の通りである。

  1. {」を打ったらその直後で改行し, 次の行は前の行の(空白以外の)頭よりもタブ一つ分(ここでは空白四文字分) 下げて始める。 (ただし,コメントは「{」の右に書いてもよい。)
  2. }」を打つときは, それに対応する「{」を含む行の(空白以外の)頭の文字とそろえる。
  3. その他の行の頭は前の行とそろえる。

(2.5.2) これらの規則はすぐに慣れることができるし, これらを守ってさえいれば,「ある中カッコとペアになる中カッコをさがす」 ことや「ある文を囲む最小の中カッコをさがす」ことはきわめて容易である。 前にも言ったが, 自分流のインデントをすでに編み出している人はそれで構わない。 そうでない人はまずはこれを覚えよ。 また,プログラムを書き上げてから最後にインデントを整えるのではなく, きちんとインデントしながら書く習慣をつけるとよい。 そのほうが,すでに書いた部分をよりよく理解できるので, より整理された頭でプログラムを書き進めることができる。

§2.6 プログラムの止め方

(2.6.1) ループの回数が多いプログラムを実行し, 「これは時間がかかりすぎだ,失敗!」と思うときがあろう。 プログラムの実行を中止してプロンプトに戻りたいときは Ctrl+C を押す。 途中でいったんとめて様子をみるときは Ctrl+S を押す。 任意のキーで再開。 (Ctrl+Q でなければ再開しないこともある。)

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

(2.7.1)

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

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

(2.7.3) 練習: 上のプログラムを改変し,(2.3.7) で説明した, 余りを求める演算子「%」 を試すプログラムを書け。

§2.8 画面制御エスケープシーケンス

(2.8.1) これは,C言語ではなく,端末の話である。

(2.8.2) ここの端末は,よその多くの端末と同じく, 「\033[」で始まる特別な文字列を画面に出力することで, 文字に色をつけたり,出力位置を変えたりすることができる。

(2.8.3) 「\033[自然数m」で文字の色などが変わる。 「\033[m」で普通に戻る。

#include <stdio.h>

main() {
    int i;

    for (i = 0; i < 256; i++) {
        printf("\033[%dm [%03d] \033[m  "  , i, i);
    }
}

(2.8.4) printf("\033[2J"); で画面全体を消去。 printf("\033[10;20H"); で次の出力位置を 10 行 20 桁めに変更。 printf("\033[2K"); で行末まで削除。


岩瀬順一