C言語は,システム記述用の言語として発達したので,文字列を扱うことが 多いということは,はじめに述べたとおりです。このような場合, 長い文字列をどこかに格納する必要が出てきます。このときに, 『文字配列』を使います。
いつものように,This is a pen. という,文字列を考えます。この文字列は,
T | h | i | s | i | s | a | p | e | n | . | \n |
ですから,あらかじめこれだけのメモリ領域(17バイト)を,文字配列 x[ ]と して用意する必要があります。
そのための,配列の宣言はこうです。
一連の行を読み込んで,一番長い行をプリントするプログラムを書け。 (行の長さでなくその内容をプリントする) |
いまのところ私達が出来るのは,簡単な関数を書くことと,教科書で やってきたことくらいのものです。簡単なことしか出来ません。それならば, 与えられた問題も簡単化してやれば良いのです。いきなりプログラム全体を 書きはじめると,玉砕は必至です。この例題をもとに,プログラムの組み方を 説明します。
教科書にはこう書いてあります(少し変えてありますが)。
while(別の行がある) if(以前に一番長かった行よりも長い) その長さを格納する その行を格納する 一番長い行をプリントする |
これで,次のページのプログラムがすぐに書けるわけではありませんが, これがスタートです。これを吟味するとつぎのような改良点(問題点?) と解決策が出てきます。
ここまで書くと,次のような全体像(すなわちmainの中身)が見えてきます。
#標準入出力ファイルの読み込み #入力可能な行長を1000文字としよう。 getline(現在の行,リミッタ) → 戻り値は,現在の行の長さ copy(出力 ,← 入力) → 戻り値は,不要 現在の行の長さ int len 今までに一番長かった行の長さ int max 現在の入力行 char line 今までに一番長かった行 char longest 今までに一番長かった行の長さを0にリセット while((getlineで現在の行長を計る)これが0以上なら)ループへ if(現在の行長>今までに一番長かった行の長さ)ならば max(一番長かった行の長さ)を現在の行長でおきかえて copyで今までに一番長かった行に現在の入力行をコピー if(行が存在した)ならば 結果(今までに一番長かった行)をプリントする |
ここで,「if(行が存在した)ならば」というのを入れたのは, 改行を一度もせずにEOFが来た場合に対応するためです。これで次のような プログラムが書けます(教科書と同じ)。
#include |
ここで,ありもしない関数getlineやcopyをつかってプログラムを 書いていることに注意! Cでは,機能さえ分かっていれば関数は使い放題, ということを思い出して下さい。後で作れば良いのです。変数もmainの中で 使うものだけを考えておけば良いのです。
さあ次は,getlineを作りましょう。最小限の機能としては,文字数の カウントプログラムを使えばいけます。
int getline(char s[ ], .....) { int c, i; for(i=0; (c = getchar())!='\n'; ++i) s[i] = c; return i; } |
となります。パラメータの char s[ ]に1000が入っていないのは,mainで既に, 定義済みだからです。第1近似ではこれで良いのですが,教科書と少し違いますね。 教科書では,
copyはもっと簡単です。ある文字配列 from[ ] から別の文字配列 to[ ] に行をコピーするということは, from[0] の文字を to[0] に代入し, from[1] の文字を to[1] に代入し,.....で, from[i] の文字を to[i] に 代入していくということです。終りはもちろん,ヌル文字 '\0' です。 教科書に対抗して,for 文で書いてみましょう。
void copy(char to[ ], char from[ ]) { int i; for(i=0; (to[i] = from[i]) != '\0'; ++i) ; } |
戻り値がない時には,明示的に 'void' と書きます。
これで,できあがりです。このように大きな所から,少しづつプログラムを 完成させていくのが,Cの面白い所です。最終的には,1画面に入る程度の 長さの関数の群れとして,プログラム(main関数)が完成するはずです。
現場での(比較的大きな)開発作業では,
|
ここも簡単に話をします。今までのプログラムの書式を思い出して下さい。
#include #記号定数の定義 プロトタイプ : : main( ) { } 関数1( ) { } : : |
main やそれぞれの関数の中で,変数が使われているのは今まで やってきたとおりです。この変数の性質をもう一度まとめると, つぎのようになります。引数と混同しないように。
これと対象的に,関数の外で定義される『広域変数』とか『外部変数』と 呼ばれる変数があります。例えば次のようにすると,mainを含む 全ての関数で使用できる『広域変数』が定義できます。
#include #記号定数の定義 『広域変数の定義』 プロトタイプ : : main( ) { } 関数( ) { } : : |
このやり方は小さいプログラムの時はある程度有効ですが,『広域変数』は,
関数から制御が離れても消えてなくならないため,プログラムの至る所で
(好むと好まざるとに関わらず)値を変えていく可能性があります。
このように「扱いにくい」変数を用いるとデバッグも大変です。『広域変数』
はなるべく使わないようにしましょう。
いわゆる,良くないプログラムなので,39ページのは説明しません。
関数の外で定義されるからといって,『記号定数』と『広域変数』を 混同しないこと! 前者はもともと定数であるという,決定的な違いが あります。『記号定数』はどんどん使用しましょう。
ちなみに,K&Rは既に古典となっており,実状(=最近の流行)とは,
あわないところが出てきているそうです。 プロトタイプ宣言が先頭にあって,main() が関数の先頭にあるのも, K&Rの流儀で,最近は, main() はいちばん最後ということが 多いようです。プロトタイプは, #include で読み込んで, 他のファイルの定義との整合を取るために使います。 将来,Cを使う時には柔軟に対応して下さい。 |