講義第8回:C言語 入門

先週までは、ここの計算機システムを使って、インターネットでの電子メイル やニュース、あるいは LATEX や tgif といった文書整形/描画ソフトといっ た、ある程度「実用的」なことを学んだ。これらの技術はこれからの学生生活 のなかで大いに活用していただきたいと思う。

で、これからの後半の講義では、「計算機はどのようにして動くか」と いう基本的な仕掛けを、自分で実際にプログラムを動かすということを通して 学ぶことにしたい。

この講義では、 C という言語を使う。この言語を使う理由はいくつかあ るが、基本的な理由は、現在もっとも広く使われているということであ る。また、現在普及しつつある C++ や Java, awk, perl のような言 語は C 言語がもとになっているものが多いので、 C を知っていれば応 用がきく。

簡単なプログラム

今日は、簡単なプログラムを例として、「とりあえず C を使ってみる」ことを目標にする。
#include <stdio.h>

void main()
{
    double a, b, c;
    scanf("%lf%lf", &a, &b);
    c = a + b ;
    printf("c = %g\n", c);
}
上のプログラムは、簡単な C プログラムである。今日は、このプログラ ムを動かしてみるということが目標である。まず、このプログラムはどういうも のかを説明しておく。

一行目は、とりあえず後で説明する。

註:計算機言語には、「そうすることに決まっているからそうしないといけな い」という類の決まりが非常に多い。これは多くの場合、もともとその言語が 設計された時にはそうするべきもっともな理由があったのだろうけれども、そ のあとの計算機ソフトウェアの変化で必要がなくなってしまったためなどである。 もちろん、そうではなくて単に言語を設計した人の趣味、あるいはなにかの理 論に基づく主張で決まっていることもある。

その次の、 void main() は、「ここからプログラムが始まる」 という宣言だと思ってもらっていい。本当はもう少しややこしい話にな るが、これも何回か後で説明する。その次のカッコ{の後から がプログラム本体で、最後の}でプログラムが終るということ になる。

次の double a,b,c; は、このプログラムではこういう「変数」を使 いますという宣言である。変数とは、計算機のメモリ中にとられる数(整数ま たは実数)をしまうための場所で、プログラムの中では名前をつけて使うこと ができる。形式は

型 名前, 名前 ... ;
というふうになる。名前はアルファベットで始まり、アルファベット または数字が続く。型は今日使うのは double (実数型)と int (整数型) である。

整数型では、数を32桁の2進数(これを普通32ビットの数という)で表す。0か ら 01....1 (1が31個並ぶ)までは普通に正の数を表し、 10...0 から 11...1 までが負の数を表現する。ただし、 11...1 が -1, 11...10 が -2, 以下 10...0が - 2の31乗 を表す。

これは現在の計算機でもっとも普通に使われている、2の補数表示という方法 である。このやり方の利点は、足し算の時には正負を考えなくて済むことであ る。他のやりかたには符号つき絶対値表示や1の補数表示があるが、いずれも 足し算のための回路が少し複雑になる。割算では余りは切捨てられる。余りを 求めることもできる。

なお、機械によっては、32桁でなくて 64桁とか16桁のものもある。

実数型では、数を符号、2進数11桁の指数、2進数52桁の仮数の3つの部分(あ わせて64ビット)で表す。10進数でいえばこれは1234という数を(+/-)1.234 X 10の3乗と表すようなものである。

次の scanf("%lf%lf", &a, &b); というところから、このプロ グラムですることが順番に書いてある。まず、最初のこの scanf なんと かというのは、「キーボードから入力した2つの数字を変数 a と b とに しまう。」という意味である。

なぜそれだけのこと書くのにこんなわけのわからないことを書かないと いけないのかとおもうかも知れないが、まあ、 C という言語ではそうい うことになっているのでしかたがない。

少し難しい書き方をすると、 scanf というのは「関数」である。 C で いう関数というのは、要するに「なにかをするための、あらかじめ誰か が作ってくれたプログラムの一部」で、scanf の場合には、要するにキー ボードから入力した数字(とは限らないが)を変数にしまうということ をやってくれるわけである。

scanf を実際にプログラムのなかで使うには、scanf(入力形式指定文字 列, 変数アドレス1, 変数アドレス2,...); という形に書く。上の例で いえば、 "%lf%lf" というのが「入力形式指定文字列」で、 「"」で囲 んで文字を並べるで、 %lf というのが、「実数一つを読む」ということ をあらわしていて、上の場合には実数2つを読むという指定をしているこ とになる。整数であれば

int i;
scanf("%d", &i);
というように、 %d と書くことになる。この d というのは「十進数」 decimal の d をとったものである。

なお、今まで「実数」といっていたが、これは正確には「倍精度実数」 と呼ばれるものである。このために double という名前が使われる。こ れに対して普通の実数(単精度実数) float というものもあるが、今回 の講義では使わない。$lf のlf は long float からとったもので、 float は floating point number 「浮動小数点数」のことである。計算 機で扱う実数のことを「浮動小数点数」といういいかたもある。

さて、そのつぎの &a, &b というのはいったいなんだろうか?

scanf は、先にいったように、キーボードから数字を読み込んで、それ を変数にしまうということをしてくれる。で、変数にしまうためには、 「どこにしまうか」を知らないといけない。

上の &a というものは、「aという変数が実際に計算機のなかのどこにあ るか」をあらわす表現である。これに対して、普通に a と書くと、プロ グラムの中では「 a という変数にしまわれている値」という意味になる。

したがって、例えば scanf("%lf", a); とか書いてしまうと、「aという 変数に値をしまう」のではなくて、aの中にしまわれている値を、変数の ある場所であると無理矢理に解釈して、そこに値にしまえ」という意味 になってしまって、全然違うことをやってくれてしまう。

その次の c = a + b; は、 変数 a と b の和を計算して c にしまう。

最後の printf("%g\n",c); は変数 c に入っている値を画面に表示する。 これも関数である。scanf とは逆に、指定にしたがって変数の値を画面 に表示する。ここでは値がわかればいいので、 &c と書く必要はない。

最初の %g は、(倍精度)実数を画面に出せという意味で、そのつぎの \n は改行しなさいという意味になっている。

まず、このプログラムをmuleで作成し、例えば example1.c といった名 前でセーブしてみよう。

ここで、いくつかの注意をしておく。

プログラムのコンパイルと実行

コンパイルとは、C で書いたプログラムを、計算機が実際に実行できる 形に翻訳する作業である。 これには gcc というコマンドを使う。(acc かもしれない、、、)これは jlatex で人間が読めるファイ ルを計算機が読める形に変換するのとおなじようなものである。

「人が読めるプログラムを計算機がいちいち読みながら実行する」ということ も可能であり、そういう風に使うのが普通である言語も多い(例えば BASIC, awk など)しかし、多くのシステムでは一旦コンパイルしたものを実行する。 これは単にその方が速いからである。

コンパイルは、シェルウインドウで

gcc example1.c  -o example1
と入れる(例によって最後にリターンする)。すると example1 という名前の 実行ファイルができる。

プログラムの修正

多くの場合、入力したプログラムは実行前に「エラーです」のようなメッセー ジがでて止まってしまう。エラーの場所に応じてmuleでプログラムを修正 し、セーブしたあともう一回 cc して見よう。

プログラムの実行

実行は、シェルウインドウで
example1
と入れる。(実際に入れるのは、 cc で -o の後に指定した名前である。これ を別のものにしていれば、別の名前を入れる。また、 -o をつけないで cc example1.c とだけしていれば、 a.out という名前の実行ファイルがで きる)。このあとリターンすると、キーボードから数字を入れるのを待ってい る状態になるので、数字をいれてはリターンするのを2回繰り返せばその2つの 和が表示されるはずである。

少しプログラムを修正して、もう少し芸のあるものにしてみよう。

/* program example 1_1  */
#include <stdio.h>

void main()
{
    double a, b, c;
    printf("Enter numbers a and b:");
    scanf("%lf%lf", &a, &b);
    c = a + b ;
    printf("c = %g\n", c);
}
これは、最初の実行文として、Enter numbers a and b: を画面に書くよ うにしてみただけである。

最初の /* ... */ はコメント(注釈)といわれるもので、コンパイラは /* で始まって */で終るところまでを無視する。このため、自分や他人 が後でもみて分かるようにするためのいろいろな説明などを書いておくこと ができる。

もう少しいろいろやってみよう。

/* program example 2  */
#include <stdio.h>

void main()
{
    double a, b;
    printf("Enter numbers a and b:");
    scanf("%lf%lf", &a, &b);
    printf("a+b = %g\n", a+b);
    printf("a-b = %g\n", a-b);
    printf("a*b = %g\n", a*b);
    printf("a/b = %g\n", a/b);
}
実行例
Enter numbers a and b:1 4
a+b = 5
a-b = -3
a*b = 4
a/b = 0.25
これは四則演算してみただけである。さらに少し変えて見よう。
/* program example 2_1  */
#include <stdio.h>

void main()
{
    int a, b;
    printf("Enter numbers a and b:");
    scanf("%d%d", &a, &b);
    printf("a+b = %d\n", a+b);
    printf("a-b = %d\n", a-b);
    printf("a*b = %d\n", a*b);
    printf("a/b = %d\n", a/b);
}
実行例
Enter numbers a and b:1 4
a+b = 5
a-b = -3
a*b = 4
a/b = 0
ここでは変数を整数型にしてみた。なお、割算の余りを求めるには % と いう演算子が使える。

例3

/* program example 2_2  */
#include <stdio.h>

void main()
{
    double a, b;
    printf("Enter numbers a and b:");
    scanf("%lf%lf", &a, &b);
    printf("a+b = %g\n", a/b);
    printf("a+b = %30.20g\n", a/b);
    printf("a+b = %30.20e\n", a/b);
    printf("a+b = %30.20f\n", a/b);
}
printf 関数での書きだし方をいろいろ変えてみた。%30.20g と書いた時 には、全部で30文字分使って、さらに小数点以下に20文字(桁)つかっ て表示せよという意味になる。 実行例1
a+b = 0.333333
a+b =            0.33333333333333331
a+b =     3.33333333333333310000e-01
a+b =         0.33333333333333331000
何も指定しないと、6桁くらいが適当にとられている。これに対し、 %30.20g とした場合は 0.33333333333333331 と、18桁まで表示される。 この後を書かないのは、上に書いたように精度が10進数で17桁くらいし かないので、指定しても無視するということである。 %30.20e の場合は、 3.33333333333333310000e-01 という風になる。この最後のe-01というの は、前の数に10の-1乗(つまりは0.1)を掛けたものが値であるというこ とを意味する。%30.20f の場合には、 e-01 とかはつけないでそのまま 表示する。

実行例2

Enter numbers a and b:10000000000 3
a+b = 3.33333e+09
a+b =             3333333333.3333335
a+b =     3.33333333333333350000e+09
a+b = 3333333333.33333350000000000000
整数型では %20d とか書いて、単にそれだけのスペースを とるという意味しかない。これはきれいに行をそろえたい時に使う。

なお、もっと複雑な式やいくつかの数学関数も使える。たとえば

c = (a+b)*(a-b);
c = sqrt(a*a+b*b);
c = exp(a)+exp(-a);
といったものを計算してみよう。sqrt は平方根、 exp は指数関数であ る。

次週予告

次週はグラフィックスの機能をを使ってみて、繰り返し処理と判断について学ぶ。