プログラミング言語 C
・ 文字配列の使い方と それを操作する関数を示すために、一群の行を読み込んで 一番長い行をプリントする プログラムを書いてみよう。
・ (全体を いくつかの部分に分けると) 一つのパートで新しい行を得て、もう一つのパートでそれをテストし、さらに もう一つのパートで (テストの結果を)格納して、残りのパートで処理を制御する (というふうに プログラムの骨格を考えてみる)。
・ まず最初に 各行を漸次 入力する関数 get_line() を書く。他のプログラムの関数としても有効な(←使える)ように、できるだけ柔軟な形に書くことにする。
- get_line(char str[], int lim) {
- int c, i;
- for (i = 0; i < (lim - 1) && (c = getchar()) != EOF && c != '\n'; ++i) {
- str[i] = c;
- }
- if (c == '\n') {
- str[i] = c;
- ++i;
- }
- str[i] = '\0';
- return i;
- }
・ 関数 get_line() は 一対の引数と 返される値とで 関数 main() とつながる。
・ これらの get_line() の引数は 最初の行で 宣言されている。
→ get_line(char str[], int lim);
・ これは、最初の引数 str は 配列であり、次の引数 lim が 整数であることの宣言である。
・ 宣言の中で 配列の大きさを与えている目的は、記憶場所の確保である。ただし 配列 str の長さは main() のなかで(すでに)決定されているので get_line() では 指定する必要はない。
・get_line() は char '\0'(←ヌル文字、値は 0)を 作成したファイルの最後におく。
・ (なぜなら)一般的に有効なプログラムとして設計するには、行の長さを返り値とし、文字列(←ファイル)の終わりでは 0 を返すようにする必要がある(からである)。
・たとえば printf() の(書式の)設定では 文字列の終わりが '\0' であることを予定している。
・ また 次の関数 copy() でも 入力引数が '\0' で終わっているという事実を使って、文字を 出力引数にコピーしている。
・ つぎに - main() で - 前に 一番長かった行よりも長い行が見つかったら、それをどこかに格納しておかねばならない。
・ ここで 2番目の関数 copy() が登場する。これは その(現在 一番長い)行を 安全な場所にコピーしておくためのものである。
・ 関数 copy() は その機能のみが使われ、値を返さない。copy() の戻り値が void になっているのは 返される値がないことを明示するためである。
- void copy(char to[], char from[]) {
- int i;
- i = 0;
- while ((to[i] = from[i]) != '\0') {
- ++i;
- }
- }
・ さいごにget_line() と copy() を制御する main() プログラムが必要である。以上の結果を示すと 次のようになる。
- /* longest_line.c */
- #include
- #define MAX_LINE 1000
- get_line(char line[], int max_line);
- void copy(char to[], char from[]);
- main()
- {
- int length;
- int max;
- char line[MAX_LINE];
- char longest[MAX_LINE];
- max = 0;
- while((length = get_line(line, MAX_LINE)) > 0) {
- if (length > max) {
- max = length;
- copy(longest, line);
- }
- }
- if (max > 0) {
- printf("Longest line of this file as under:-\n");
- printf("%s", longest);
- }
- return 0;
- }
- get_line(char str[], int lim) {
- int c, i;
- for (i = 0; i < (lim - 1) && (c = getchar()) != EOF && c != '\n'; ++i) {
- str[i] = c;
- }
- if ( c == '\n') {
- str[i] = c;
- ++i;
- }
- str[i] = '\0';
- return i;
- }
- void copy(char to[], char from[]) {
- int i;
- i = 0;
- while ((to[i] = from[i]) != '\0') {
- ++i;
- }
- }
・ ついでに、だいじなことを いっておきたい。(それは)これほど 小さなプログラムであっても、設計上の やっかいな問題を提起している、ということである。
・ 例えば、制限されている行の長さよりも 大きな行を読み込んだときに 関数 main() では どう(処理)すべきなのか ?
・ 関数 get_line() では、配列がいっぱいになると 改行文字の有無を問わずに 入力を打ち切って、安全に作動する。
・ (なぜなら) get_line() では 前もって 入力行の長さを知る方法がないので、オーバーフローをチェックする必要がある(からである)。
・ (それに対して すでに長さがわかっている) 関数 copy() では エラーチェックを付け加えることはしていない。
・ (話を簡単にするため この問題は無視するが) main() では、返ってきた(行の)長さと 最後の文字を見て 行が長すぎたか否かを判断し、それによって (オーバーフローに)対処することが 可能である。(p35-38)
$./longest_line < longest_line.c として確認。
う〜ん、どこが「話を簡単にするため」なのか よくわからん . . .
(追記) コメントをつけると繁雑になりそうなので、注意が必要(?)なところだけ コードの色を変えてみました。