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まで