プログラミング言語 C

ポインタ それ自体は 変数であるから、他の変数と まったく同様に 配列に 格納することができる。 このことを、一連のテキスト行を アルファベット順に 分類する プログラム、すなわち UNIX の プログラム sort の (余分な機能を 取り去った) 裸の version を 書くことで 示してみよう。

この場合、可変長の テキスト行を 効率よく、かつ 便利に扱う データ表現が 必要となる。

分類される 全行が、1つの長い 文字配列内に 始めから終わりまで 格納されるとすると、各行は その先頭文字への ポインタで アクセスできる。 2つの行は そのポインタを (関数) strcmp に 渡すことによって 比較 (することが) 可能である。 順番に なっていない 2つの行を 交換するときには、テキスト行 自体ではなく、ポインタ配列内の ポインタを 交換すればよい。

これによって、実際の行を 動かすための 複雑な 記憶管理と high overhead という 2つの 問題が 解消されるのである。

さて、ここでの ソートプロセスは 次の 3つの ステップからなる。

入力全行を 読み込む → それを ソートする → 順番に プリントする

ソートのステップは ちょっと 後まわしにして、データ構造 および 入出力に 注意を 向けてみよう。

入力ルーティンでは 各行の文字を 集めて 格納し、各行への ポインタの配列を つくらなければならない。 ソートやプリントで 実際に 入用となる 情報として、入力した行数も 数える必要がある。 関数では 有限個の 行数を 扱えるだけだから、入力が 多すぎたときは -1 のような 規定外の数を 返すことができる (ようにする)。


#define MAX_LINES 5000 /* max #lines to be sorted */
char *line_ptr[MAX_LINES]; /* poiinters to text lines */

#define MAX_LEN 1000 /* max length to any input line */
int get_line(char *, int);

int read_lines(char *line_ptr[], int max_line)
{
int len, n_lines;
char *p, line[MAX_LEN];

n_lines = 0;
while )((len = get_line(line, MAX_LEN))( > 0)
if (n_lines >= max_lines || (p = malloc(len)) == NULL)
return -1;
else {
line[len - 1] = '\0'; /* delete newline */
strcpy(p, line);
line_ptr[n_lines++] = p;
}
return n_lines;
}

int get_line(char p[], int limit)
{
int c, i;

for (i = 0; i < limit -1 && (c = getchar()) != EOF && c != '\n'; ++i)
s[i] = c;
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';

return i;
}

ここでの 新しい要点は、line_ptr の 宣言である。(← ポインタの配列 つまり ポインタのポインタ)

char *line_ptr[MAX_LINES]

は line_ptr が MAX_LINES 個の 要素をもつ 配列であり、その各要素が char への ポインタであることを 示す。 すなわち、line_ptr[i] が 文字へのポインタであり、*line_ptr[i] は それが指す文字、すなわち i番目に 格納された テキスト行の 文字である。

line_ptr 自体は 配列の名前であるから、ポインタとして 扱うことが 可能で (出力行を 書き出す) write_lines は 以下のように 書くことができる。


void write_lines(char *line_ptr[], int n_lines)
{
while (n_lines-- > 0)
printf("%s\n", *line_ptr++);
}

*line_ptr は 最初、先頭の行を 指している。 インクリメントするごとに、それは 次の行に 進められ、同時に n_lines (行数の合計) も カウントダウンされる。(p131-134)

(追記) ここで read_lines での malloc の扱いを 参考にした Answers to Exercise には、K&R chap.5-7 で 扱っている 二次元配列を 使った例が 載っています。
http://users.powernet.co.uk/eton/kandr2/krx507.html