汎用queueの開発

Javaなどのオブジェクト指向の言語での開発に慣れてくると、C言語で プログラムを書こうとしても、あれもない、これもない、でコードを書くのが いやになる事があります。というか、最近なりました。 たとえばqueueなんかはJavaだとList(ArrayListとか)を使うだけですが、 Cでは一々コードを書かなければなりません。生産性が下がるだけでなく、 コードの品質を保つのもなかなか面倒です。

そこで、JavaやC++のクラスライブラリのノリで使えるライブラリを志向して、 まずは汎用のqueueを作ってみました。

queueというのは説明するまでも無く、FIFO(First In, First Out)を実現する データ構造です。チューブの右からデータを入れ、左から取り出すような イメージですね。チューブは空かもしれませんし、既に沢山データが入っている かもしれません。左から取り出す毎にチューブの中のデータは少しずつ左に 移動してゆきます。データを入れるタイミングと取り出すタイミングは同期して いるとは限らないので、チューブ内のデータの量は増減します。queueのデータ 構造はシステムのあらゆる局面でしばしば登場するものです。

queueを実現する典型的な方法は二重リンクリストを使うものです。queue内の 個々のデータの構造を、

typedef struct general_queue_item {
	struct general_queue_item *prev;
	struct general_queue_item *next;
	void *data;
} general_queue_item_t;
という風に定義します。nextでgeneral_queue_item_tの前方向リンクリストを、 prevでgeneral_queue_item_tの後方向リンクリストを作るようにします。 汎用的に使うためにデータの型はvoid*にしています。

これで作るqueueの先頭と末尾を管理するデータ構造を定義します。 queueの先頭からnextをたどってゆくと末尾に、末尾からprevをたどって ゆくと先頭に達するようになればよいわけです。

typedef struct general_queue_info {
	general_queue_item_t *head;
	general_queue_item_t *tail;
} general_queue_info_t;
てな感じ。

では関数はどうなるでしょう。queueにデータを入れる方は、

void push_item(general_queue_info_t *info, void *data)
{
	general_queue_item_t *q;

	/* ここでqにgeneral_queue_item_t型のデータを一つ用意 */

	// queueの末尾につける
	q->data = data;
	q->next = NULL;
	q->prev = info->tail;
	if (info->tail != NULL) {
		info->tail->next = q;
	}
	info->tail = q;

	// queueが空だったら、先頭にも設定してあげる
	if (info->head == NULL) {
		info->head = q;
	}
}
となります。末尾に要素を一つつけるのですが、つける要素だけ でなく、現在の末尾の要素の中も書き換えないと二重リンクを維持 できません。また初期状態(queueが空)の処理も注意が必要です。

取り出すほうはどうすればよいでしょうか。

void *get_item(general_queue_info_t *info)
{
	general_queue_item_t *q;
	void *data;

	// 先頭がNULL(==queueが空)ならNULLを返す
	if (info->head == NULL) {
		return NULL;
	}

	// リンクの付け直しをする
	q = info->head;
	data = q->data;
	info->head = q->next;
	if (info->tail == q) {
		info->tail = NULL;
	}

	return data;
}
てな感じです。こっちも先頭から取り出したとき、残ったqueue が空になる場合に注意が必要です。

queueの各要素のデータ領域をどこから持ってくるのか、複数のqueueを扱う場合 の考慮などを行うと、次のプログラムができました。どうでしょうか


機器ソフト開発室  PoisonSoft