すべてのリスト(queue?)は、先頭、あるいは先頭と末尾の構造体(element)を指し示す 「???_HEAD」という名前のマクロで定義される構造体を持つ。
各要素(element)は、「???_ENTRY」というマクロを、使用する構造体の中にメンバとして持つ必要がある。
HEAD用の構造体 (HEAD) ↓ 要素用の構造体1 (含ENTRY) ↓ 要素用の構造体2 (含ENTRY) ↓ 要素用の構造体3 (含ENTRY) ↓ : ↓ 要素用の構造体N (含ENTRY)LIST を例に取って解説すると...
	#define LIST_HEAD(name, type)						\
	struct name {								\
		struct type *lh_first;	/* first element */			\
	}
	#define LIST_HEAD_INITIALIZER(head)					\
		{ NULL }
	#define LIST_ENTRY(type)						\
	struct {								\
		struct type *le_next;	/* next element */			\
		struct type **le_prev;	/* address of previous next element */	\
	}
以上が sys/queue.h に定義されている(宣言用の)マクロで、
実際に C で使うためには、以下のようにする。
	/* HEAD の宣言と定義 */
	LIST_HEAD(,my) my_head =
	    LIST_HEAD_INITIALIZER(my_head);
	/* 構造体(element)の宣言 */
	struct my {
		char *my_member1;
		short my_member2;
		LIST_ENTRY(my) my_list;	/* LIST_ENTRY の場所はどこでも可 */
		int my_member3;
		double my_member4;
	};
あとは LIST_INSERT_AFTER, LIST_INSERT_BEFORE, LIST_REMOVE
などのマクロを使って操作を行えばいい。
	void my_add(void)
	{
		struct my *myp = malloc(sizeof(struct my));
		memset(myp, 0, sizeof(struct my));
		myp->my_member1 = ...
		myp->my_member2 = ...
		myp->my_member3 = ...
		LIST_INSERT_HEAD(my_head, myp,my_list);
	}
	void my_remove(struct my *myp)
	{
		LIST_REMOVE(myp, my_list);
	}
などなど。
#define LIST_HEAD(name, type)				\
struct name {						\
	struct type *lh_first;	/* first element */	\
}
#define LIST_ENTRY(type)				\
struct {						\
	struct type *le_next;	/* next element */	\
	struct type **le_prev;	/* address of previous next element */	\
}
シンプルかつ必要十分なリスト構造。リストを前後に辿ることはできるが、リストを辿らずに末尾を知ることはできない。
LIST_HEAD は最初の element へのポインタのみ持つ。
ENTRY には、次の element へのポインタと、自分を指しているポインタへのポインタ
(前の element へのポインタで無いので注意)を持つ。
(自分を指しているポインタへのポインタは、INSERT_BEFORE のために必要)
LIST_PREV は存在しないが、がんばれば、LIST_PREV も実現できなくはない。
以下のようなマクロを使えば OK。(ただし head を知ってなければいけない)
#define OFFSETOF(type, member) ((size_t)(&((struct type *)0)->member)) #define LIST_PREV(head, type, elm, field) \ ((((struct type *)((elm)->field.le_prev)) == ((struct type *)(&(head)))) ? \ (struct type *)NULL : \ ((struct type *)(((char*)((elm)->field.le_prev))-(OFFSETOF(type,field)))))
 
 
 
#define SLIST_HEAD(name, type)						\
struct name {								\
	struct type *slh_first; /* first element */			\
}
#define SLIST_ENTRY(type)						\
struct {								\
	struct type *sle_next;	/* next element */			\
}
SLIST は、LIST から prev のポインタを省略したもの。HEAD には最初の element へのポインタ、ENTRY には次の element へのポインタだけを持つ。
そのため、PREV も INSERT もできない。INSERT_HEAD と REMOVE_HEAD しかできないので、用途がかなり制限される。
しかし何故かマクロでは REMOVE が定義されている。
どうやって実装しているのかと思って中身を見ると、HEADから該当elementまでリストを辿るというアホなことをしている。
これはダメだろ常識的に考えて…。
SLIST_REMOVE は小さなリストでしか使ってはいけません。
 
 
 
#define SIMPLEQ_HEAD(name, type)					\
struct name {								\
	struct type *sqh_first; /* first element */			\
	struct type **sqh_last; /* addr of last next element */ 	\
}
#define SIMPLEQ_ENTRY(type)						\
struct {								\
	struct type *sqe_next;	/* next element */			\
}
末尾をわかるようにしたSLIST。
SIMPLEQ_HEAD は、最初と最後の element へのポインタを持っている。
ただし、最後のポインタ(sqh_last) は、最後の element の SIMPLEQ_ENTRY のポインタである。
SIMPLEQ_ENTRY には、次の element のポインタだけを持つ。LIST や他の queue のように
自分を差しているポインタへのポインタは持たない。
したがって、任意の element の前に element を挿入すること(SIMPLEQ_INSERT_BEFORE)はできない。
上述のように sqh_prev 相当のものを持たないので、SIMPLEQ_REMOVE さえできないことに注意。
SIMPLEQ_REMOVE_HEAD で、先頭から削除することしかできない。
もし任意の要素を REMOVE したいのならば、先頭から該当 element まで辿って…、みたいなことをしなければならない。
当然 SIMPLEQ_PREV も不可能。
これも先頭から辿ればできなくはないけど。
 
 
 
#define TAILQ_HEAD(name, type)                                  \
struct name {                                                   \
        struct type *tqh_first; /* first element */             \
        struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_ENTRY(type)                                              \
struct {                                                               \
        struct type *tqe_next;  /* next element */                     \
        struct type **tqe_prev; /* address of previous next element */ \
}
LIST の HEAD に、末尾の element の TAILQ_ENTRY を指すポインタ(tqh_tail)を加えたもの。
しかし、たったこれだけの変更で、常に tqe_prev->tqe_prev と辿れることが保証され、
tqe_prev->tqe_prev->tqe_next と辿ることにより、一つ前の element を得ることができるようになってる。すごい。
(図参照。HEAD では tqe_next = tqh_first、tqe_prev = tqh_last であることを利用)
#define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
構造的には、かなり万能なリスト構造である。無理に欠点を探すとしたら
 
 
 
#define CIRCLEQ_HEAD(name, type)                            \
struct name {                                               \
        struct type *cqh_first;         /* first element */ \
        struct type *cqh_last;          /* last element */  \
}
#define CIRCLEQ_ENTRY(type)                                    \
struct {                                                       \
        struct type *cqe_next;          /* next element */     \
        struct type *cqe_prev;          /* previous element */ \
}
各ENTRY 内の prev の差し示す先を、「前の element の ENTRY へのポインタ」
の代わりに「前の element のポインタ」にしたもの。
一見便利そうだが、 先頭 element の ENTRY の prev が、HEAD を差してしまっていたり、 末尾 element の ENTRY の next が、HEAD を差してしまっていたりして、結構イリーガル。
つまり先頭 element から prev を辿るときに、その先が HEAD か element かで 場合分けしなければならないし、末尾の element から next を辿るときに、 その先が HEAD か element かで場合分けしなければならない。
しかも場合分けしなければならないということは、INSERTやREOVE時にHEADを知っておく必要がある。
つまり element だけが渡された関数内で、CIRCLEQ_INSERT や CIRCLEQ_REMOVE することができない。
微妙に使いにくい。
他の queue と違い、link に関する全ての情報を持っているので、前後、先頭末尾と、 好きに辿ることができる。
 
 
 
| LIST | SLIST | STAILQ (=SIMPLEQ) | TAILQ | CIRCLEQ | |
| headerのサイズ | sizeof(void*)*1 | sizeof(void*)*1 | sizeof(void*)*2 | sizeof(void*)*2 | sizeof(void*)*2 | 
| elementのサイズ | sizeof(void*)*2 | sizeof(void*)*1 | sizeof(void*)*1 | sizeof(void*)*2 | sizeof(void*)*2 | 
| _INIT | O(1) | O(1) | O(1) | O(1) | O(1) | 
| _EMPTY | O(1) | O(1) | O(1) | O(1) | O(1) | 
| _LAST | - | - | O(1)※ | O(1) | O(1) | 
| _FIRST | O(1) | O(1) | O(1) | O(1) | O(1) | 
| _NEXT | O(1) | O(1) | O(1) | O(1) | O(1) | 
| _PREV | O(1)※ | - | - | O(1) | O(1) | 
| _INSERT_HEAD | O(1) | O(1) | O(1) | O(1) | O(1) | 
| _INSERT_TAIL | - | - | O(1) | O(1) | O(1) | 
| _INSERT_AFTER | O(1) | O(1) | O(1) | O(1) | O(1) | 
| _INSERT_BEFORE | O(1) | - | - | O(1) | O(1) | 
| _REMOVE | O(1) | O(n) | O(n) | O(1) | O(1) | 
| _REMOVE_HEAD | O(1)※ | O(1) | O(1) | O(1)※ | O(1)※ | 
| _FOREACH | O(n) | O(n) | O(n) | O(n) | O(n) | 
| _FOREACH_REVERSE | - | - | - | O(n) | O(n) | 
| LIST | SLIST | STAILQ (=SIMPLEQ) | TAILQ | CIRCLEQ | |
| _INIT | Yes | Yes | Yes | Yes | Yes | 
| _EMPTY | Yes | Yes | Yes | Yes | Yes | 
| _LAST | - | - | Yes | Yes | Yes | 
| _FIRST | Yes | Yes | Yes | Yes | Yes | 
| _NEXT | no | no | no | no | no | 
| _PREV | Yes | - | - | Yes | Yes(※) | 
| _INSERT_HEAD | Yes | Yes | Yes | Yes | Yes | 
| _INSERT_TAIL | - | - | Yes | Yes | Yes | 
| _INSERT_AFTER | no | no | Yes | Yes | Yes | 
| _INSERT_BEFORE | no | - | - | no | Yes | 
| _REMOVE | no | Yes | Yes | Yes | Yes | 
| _REMOVE_HEAD | Yes | Yes | Yes | Yes | Yes | 
| _FOREACH | Yes | Yes | Yes | Yes | Yes | 
| _FOREACH_REVERSE | - | - | - | Yes | Yes | 
PREVを辿ったり、最後尾にアクセスしたければTAILQを使う
各要素中のエントリを小さくしたいのであれば、SLIST/SIMPLEQを考える。
ただし、SLIST/STAILQ/SIMPLEQのREMOVEはコストが高いので事実上小さなリストでしか使えない。
巨大なリストでは REMOVE_HEAD しか使えないと思うべし。
SLISTとSTAILQはENTRYにポインタが一つだけなのが利点(たぶん)。 各要素を小さくしつつ、FILO を構成したい時はSLIST。 各要素を小さくしつつ、FIFO/FILO (INSERT_HEAD, INSERT_TAIL, REMOVE_HEAD) したい時は STAILQ, SIMPLEQ。
TAILQがあればCIRCLEQは要らない子。
