ポインターとデータ構造

データ構造

例:算術式
a * b + c * d - e
-> (((a,*,b),+,(c,*,d)),-,e)
基本構造は (x,y,z) y は演算子、x,z は被演算数。 struct 風に書くと、
struct Enzan {
	Hi_enzan_suu x;
	Enzan_shi    y;
	Hi_enzan_suu z;
};
これを計算の手順と考える。次の例では、同じ計算が出てくる。
a * b + a * b * c
-> ((a,*,b),+,((a,*,b),*,c)))
同じ物が2度以上出てくることを考え、算術式に名前づけを おこなう。
(A,(B,a,*,b),+,(C,B,*,c))
この場合構造は(n,x,y,z)で n はこの部分算術式につけられた名前。 struct 風に書くと、
struct Enzan {
	struct Enzan *n;
	Hi_Enzan_suu  x;
	Enzan_shi     y;
	Hi_Enzan_suu  z;
};

長さが不定なデータ構造

長さが不定なデータ構造(例えば誕生日を入れた名簿)を作ることを考える。 再帰的な型定義が使える場合、
struct s {
	struct a {
		int year,month,day;
		char name[40];
	} birthday;
	struct s next;
};
のようにすると、「たくさんの」誕生日と名前を連ねることができる。が、 この構造の終わりを定義できない。ここで、next を次の名簿の「名前」 とすると、構造自体は完結することができる。この「名前」をポインタ と呼ぶ。
struct s {
	struct a {
		int year,month,day;
		char name[40];
	} birthday;
	struct s *next;
};
C言語におけるポインタ関係の演算には以下のようなものがある。
&
変数・関数名のポインタを取り出す
*
ポインタの示す変数・関数の実体
(int *) etc
ポインタの型変換
=, == etc
代入・比較など
++,--,-, etc
加算・減算など
->
ポインタの示す構造体のメンバ

線形リスト

線形リスト(linear list)は「次のデータへのポインタ」を構造体の メンバとしてもっている。普通、最後のデータであることを示すた めには NULL ポインタ(nil)が使われる。線形リストを使った スタック(LIFO バッファ)のプログラム例を示す。
プログラム例1
プログラム例2

双方向リスト

双方向リストは、前方への探索とともに後方への探索も できるように、前後2つのリストへのポインタを持っている。 次のプログラムは双方向リストを使った待ち行列(Queue, FIFO バッファ)の例である。
プログラム例3
このプログラムで空の待ち行列は下図のように表される。
ここで、待ち行列にデータが入ると、新しいリストを作成し、 ポインタを以下のように書き換える。

待ち行列からのデータの取り出しは、以下のように右端 のリストから行われる。データを取り出して不要になった リストは free()によって削除される。


問題

  1. 上のスタックのプログラム例では、スタックが空の時に pop()したときのエラーチェックを行っていない。エラーを 検出して端末に出力し終了するようにプログラムを書き換えよ。
  2. 一般にはリスト構造を使わずに十分な配列を用意することで、 スタックを実現することが多い。この方法でプログラムを作成 せよ。
  3. C言語の main関数を、
    main(int argc, char **argv)
    で呼び出すとコマンドラインからの引数を渡すことができる。 ここで、argc は引数の数であり、argv は引数の文字列(ポインタ)への ポインタである。引数を表示するプログラムを作りなさい。