c言語入門
教養ゼミ コンピュータビジョンとその周辺 教材
2000.06
・計算機のプログラムに習熟すると、車にたとえればエンジンや車体を
自由に作れることに相当し、自分の望むシステムを自由に作ることができ
るようになる。工学を学ぶ者にとっては必須の教養の1つである。ここで
はUNIXのもとで重要な計算機言語であるcについて学ぼう。
・世界に向ってこんにちは
つぎのプログラムでhello worldがプリントされる:
#include <stdio.h>
main()
{
printf("hello world!\n");
}
ここでprintfは文字を端末に表示するための関数と呼ばれるものであ
る。また、\nは1行改行するためのものである(ここには\(バックスラッ
シュと読む)と書いたが、通常\と書いても同じ意味となる)。一般に上
記の形式で" と "で囲まれた範囲の文字が表示される。なお、
printf("hello world!\n");のようなものを文と呼ぶ。各文の最後に
は;(セミコロン)をつける。hello world!の部分に別のものを入れて
試してみよう。
一般にプログラムを作成して完成するまでには、
(1)プログラム(ソースプログラムという)をエディタ(emacsなど)で入
力(または修正)する(c言語のソースプログラムのファイル名は
hello.cのように必ず.cで終るようにすること)。
(2)cc ソースプログラムファイル名 によって実行可能な形式に直す(コ
ンパイルするという、細かくはリンクという手順も内部に含まれている。
なお、コンパイルは通常ccで行うことが多いが、gccととすることもあ
る)。できあがった実行可能プログラムはa.outという名前で格納され
る。
(3)プログラムを実行する(コマンド入力可能な状態(コマンド行という)
でa.outと入力しreturn(enter)キーを押せばよい)。
この間、ccでエラーが検出されることがある(コンパイルエラー)、そ
の場合(1)にもどり、エディタで正しくなるように修正する。また、(3)の
実行に至っても意図した結果が得られないこともある(これを実行時誤り
という)。処理手順の考え方(アルゴリズムという)に誤りがあることが
多い。また、コンパイラ(ここではccのこと)が検出できない変数の初期
値が妥当でない場合や配列と呼ぶ一連のデータの配列番号が許されない値
をとっているなども実行時誤りとなる。いずれにせよ、(1)にもどってや
り直す。大きなプログラムになると、プログラムが正しく働くことを種々
検査し合格してはじめて完成となる。
・繰返し
上記のプログラムを変形して繰返して表示させて見よう:
#include <stdio.h>
main()
{
int i;
for( i = 1 ; i <= 10 ; i = i +1){
printf("hello world!\n");
}
}
for文は、その後ろの{ と }で囲まれた部分(文ブロック)を繰返す。
この例でi = 1 を初期条件、i <= 10を継続条件、 i = i +1を増分と
呼ぶ。 i = i +1は単にi++と書いてもよい。int i; とあるのはiが整
数であることを宣言するもので、ある変数を使うときは必ず宣言しなくて
はならない。intの他にfloat(浮動小数点数(25.37のような小数点で
区切られる数の表現))もある。上記のfor( i = 1 ; i <= 10 ; i
= i +1){で10を変えるとどうなるか試してみよう。
c言語では空白文字(スペース)や空白行は通常意味を持たないのでプ
ログラムを見やすくするため適宜追加削除してよい。また、プログラムを
見やすくするため、字下げして縦方向に同じ性質のものを揃えるのでこれ
を励行するとよい。上記の int i; を入れるとき改行直後にtabキーを
押すと適当な文字数右に送ってくれる。また、for後の{ と } で囲まれる
文ブロックの範囲では、更にtab1つ分右側に字下げする(多分エディタが
文ブロックを理解して自然に余分に右に送ってくれるはず)。
・1〜10まで足して見よう
上のプログラムの変形で足し算(累算)ができる。
main()
{
int i, sum;
sum = 0;
for(i = 1 ; i <= 10 ; i = i +1){
sum = sum + i;
}
printf("answer= %d\n", sum);
}
sum = 0; の文でsumなる変数を0にしている(初期化するという)。
printfの中の%dは整数を表示するためのものである。この仲間に浮動小
数点数表示の%fなどがある。
printfでは、
printf("message1 %d message2 %d message3 %d\n", i1, i2 ,
i3);
とするとmessage1 i1の中身 message2 i2の中身 message3 i3の中
身 の順で表示される。" と " で囲まれた中を左から順に調べ、普通の文
字ならそのまま表示し、%が出てくると、その度に"…"の後にある変数を
1つづつ見にいくと理解すればよい。なお%や\は特殊文字で、それが出
てくると特別のことをすると約束されている。
・関数
ある操作をひとまとめにして関数の形式にまとめると、外からはブラッ
クボックスに見えて都合がよい。関数の呼出しは、プログラム中で引用す
ることで行われる。通常のプログラムは上から下へと順に制御が移るが、
関数を引用すると関数に制御が渡される。それが終ると呼び出した元のプ
ログラム(の呼出した次の行)に制御がもどる。
例
#include <stdio.h>
int sub1( int i );
main()
{
int i, ans;
i = 10;
ans = sub1( i );
printf("answer= %d\n", ans);
}
int sub1( int i)
{
int n;
n = i * 2;
return n;
}
制御は、mainプログラム(これも形式的には関数の一種である、ただ
し名前は必ずmainとする)のans = sub1( i ); のところでint sub1
( int i); からはじまるsub1なる関数に移る。
数学で、ある与えられた数に対し答を与えるものを関数と呼ぶが、c言
語の関数も基本的にこれと同じと考えてよい。上記では、与えられたiに
対してsub1( i )なる答(iの2倍)を返す。その値は内部で計算して
return n; の文で返している。一般に関数値はこのような形式で返すこ
とができる。なお、ここに n = i * 2; の*は掛算の意味で用いている
(計算機言語では*をこの意味で用いるのが普通)。
この例の2行目でint sub1( int i ); とあるが、これはこのプログ
ラム中でsub1という関数を使うという宣言(プロトタイプという)であ
る。先頭のintはこの関数が整数型の答を返すことを意味している。また
かっこ内のint i は、この関数が整数型の数に対して答を与えるもので
あることを宣言している。このような関数に与える数値を引数(ひきすう)
と呼ぶ。一般に引数は複数個あってよく、整数や浮動小数点数が混在する
こともある。
関数の本来の意味からすれば答を関数値として返すのが自然だが、返さ
ないものもある(例えばprintfは答を返さない。なお、関数で答を返す
以外の作用を副作用という)。この場合関数の前にvoidをつける。また
引数のない関数もある。この場合引数の位置にvoidと書く(例 void
sub(void))。
・makeについて
料理を作るときに用いる手順書をレシピというが、それに相当する
Makefileというものにやるべき手順を書いておく。makeというコマンドを
実行するとMakefileの指示に従って作業してくれる。コンパイルして実行
ファイルを作るには、ccに対する複雑なパラメータの指定が必要で、また
大きなプログラムは部分に分けて分割コンパイルすることが多い。よって、
少しまともなプログラムを作ろうというときにはmakeコマンドのお世話
になる。
・デバッガ使用の勧め
少し複雑なプログラムを作る場合、予期せざる誤りを取除く操作(デバッ
グ)を完成するまで何度も繰返すことになる。文法上の誤りはcコンパイ
ラが指摘してくれるが、文法上は正しくても思わぬ思い違いなどから求め
る結果が得られないことがよくある。このようなとき、途中の経過(どの
段階で変数の値がいくらになっているかなど)をプリントさせてみたりす
ると(また、もし可能ならプログラムの実行を途中で止めてみる)プログ
ラムの働きを確認でき早くデバッグできる。そのためにプログラムに本来
は必要ないprintf文(時として入力文も)などを挿入することも行われ
る。これも役立つが、もっと便利なやり方がある。それがデバッガである。
・デバッガの使い方
コンパイルするときccに代えてcc -gとする(これをデバッガオプション
付のコンパイルという)。デバッガオプション付のコンパイルを行うと多
少実行速度が遅くなるが、できあがった実行プログラム(a.out)をその
まま通常どおり実行することもできる。
デバッグモードの実行は次のように行う:
(以下で説明するdbxコマンドを使うためには設定が必要となる場合が
ある。Solarisのもとで、コマンドパスに/opt/SUNWspro/binを追加する。
詳細は別に説明する)
コマンドが実行できる状態(コマンド行という)でdbx a.out と入力す
る。実行プログラムが読込まれデバッグモードとなる。この状態で種々の
dbxのコマンド(正しくは通常のコマンドより1レベル下のものなのでサ
ブコマンドという)が入力できるようになる。dbxコマンドの主なものは:
help :dbxコマンドとしてどんなものがあるか教えてくれる。helpに続
けてdbxコマンド名を入れればdbxコマンドの使い方を教えてくれる。
run :プログラムを開始する。どこか途中で止っているときはご破算で
やりなおしとなる。
list :ソースプログラムを番号付で表示する(この番号をよく使う。な
お、番号付のリストを手元に置きたいときcat -nコマンドを利用できる)。
print :指定した変数の値を表示する。
stop at :指定した番号に制御が移るとき実行直前で止る。
stop in :指定した関数に制御が移るとき実行直前で止る。
step :止っているとき、1ステップだけ次に進む。
next :stepと似ているが、stepは実質的な1ステップ次に進む(例えば関
数呼出しの直前で止っているとき関数の中に入って止る)が、nextはソー
スプログラム上で次へ行く(関数の中へ入り込まないで、形式的にその行
を実行する)。
cont :止めたプログラムを継続する(従って次にstop atやstop inに出会
うまでどんどん進む)。
quit :デバッガを終了する。
デバッガを使いこなすとプログラムのデバッグが早くできるし、プログ
ラムのはたらきを深く理解できる。なお、システムに備えられている関数
はデバッグモードでコンパイルされていないので、中に入って見ることが
できない。(注意、デバッグモードでの実行はスピードを除きほぼ完全に
通常の実行と同じと考えてよいが、ごくまれに結果が異なることがある
(似たような事情は実行速度を上げるためのオプティマイゼーション付コ
ンパイルでも存在する)。)
・再帰プログラムに挑戦して見よう (やる気のある人に)
c言語では再帰呼出しというのができる。これは数学の漸下式に似てい
る。例えばn!(nの階乗)は1からnまでの掛算と定義されるが、
n! = n * (n-1)! とも書ける。
10!は9!から計算でき、さらに9!は8!から計算できる。このようなた
らい回しの結果10!が得られる。ただし永久にたらい回しでは-∞!に至っ
てしまう。そこで1! = 1として、それを検出したら終るようにする。
一般に再帰プログラムは
(1)呼び出されたら終端条件に至っているかチェックし、そうなら定義さ
れた値を与えて終る。
(2)そうでない場合、定義に従ってたらい回しして自分自身を呼び出す。
というように書けばよい。
再帰プログラムを用いると簡潔にプログラムを書けることがある。また
パズル的なプログラムも再帰プログラムで書けることがある。種々挑戦し
てみてもらいたい。なお、n!はすぐ計算機がパンクする(といってもそ
のプログラムが異常終了するだけで他人に迷惑は掛らないが)のでnの値
はあまり大きくしないこと。nを大きくしても問題のない簡単な例はnま
での累算(sum(n) = sum(n-1) + n)である。例えば、1から10までの
足し算を再帰プログラムで書き、前述したストレートなプログラムと結果
を比較してみよう。また、デバッガで動作を追いかけると再帰プログラム
がどのように動くか分って勉強になる(次々とたらい回ししていく様子、
また終端条件に達して呼ばれた側にもどっていく様子は感動もの!)。
再帰というのは関数の呼び出しに関するものと理解してほしい。数学で
言うと漸化式に当たる。例えば階乗n!は1からnまで順に掛けたものだと定
義されるが、これを
n! = (n-1)! * n
と捉えることができる。この式には、仮にn!が(関数の形で)定義され
ているなら、 n!を計算するのに直接計算せず、(n-1)!を計算して、その結
果にnを掛ければ答が出る ことを示している。
ある計算をするプログラムのまとまりをc言語で関数と呼ぶが、今仮に
階乗を計算する関数をkaijo(n)と表す。c言語では、プログラムの中に関
数を書くと、それが呼び出され答えが返る。
例えば
n = 10;
kotae = kaijo(n);
とすれば、kotaeに10!が得られるようにできる。
この呼ばれる側のkaijoのプログラムが一番肝心のところである。
これを
ans = 1;
for(i =1; i <= n; i++){
ans = ans * i;
}
としては再帰とは言わない。自分をきめるのに自分自身を使うというの
が再帰の考えである。
となると、
ans = kaijo(n-1) * n;
がkaijoの心臓部となる。もし、nなる値でkaijoが呼ばれたら、n-1でま
たkaijoを呼び出す。そうしたら、プログラムは自然にn-2でkaijoを呼び
す。これがずっとくり返される。このままだと、nの値がどんどん小さ
くなって、最後は-∞になってしまう。c言語では-∞は表せないので、
プログラムは暴走し、答えも得られない。
そこで、1!=1と定義しておく。kaijoのプログラムの最初にこの条件を
みたしているか調べ、そうなら答として1を返すようにするのである。
デバッガの使い方を覚えて、再帰のプログラムをデバッガのもとに動か
してみると理解が一層深まる。
参考書
1.カーニハン、リッチー著 石田訳:プログラミング言語C、共立出版、
2800円
2.J.Purdum著 石原訳:Cプログラミング、丸善、2678円
3.カーニハン、パイク著 石田晴久訳:UNIXプログラミング環境、ア
スキー出版局、3800円
1.はc言語の開発者自らが書いたもので権威がある(著者の頭文字をとっ
てK&R本といわれることがある)。c言語の相当のベテランが見ても
参考になるヒントがたくさんある。ただし、全部がつながっているの
で、途中から読み始めたり、辞書的に利用するには適さない。
2.は教科書的に書かれた名著、説明がていねいで初心者の陥りがちな点
への注意が述べてある。途中から読んでもハンドブック的にも使える。
3.c言語とは離れるがUNIXのコマンド体系について学ぶのに適する名著
である。これもUNIXの開発者が書いている(K&P本)。
質問・連絡は oshima%kaiyodai.ac.jpまで