概述
sys/queue.h是LINUX/UNIX系統下面的一個標准頭文件,用一系列的數據結構定義了一隊列。包括singly-lined list, list, simple queue(Singly-linked Tail queue), tail queue, circle queue五種。
引用此頭文件對這五種數據結構的描述:
A singly-linked list is headed by a single forward pointer. The
elements are singly linked for minimum space and pointer manipulation
overhead at the expense of O(n) removal for arbitrary elements. New
elements can be added to the list after an existing element or at the
head of the list. Elements being removed from the head of the list
should use the explicit macro for this purpose for optimum
efficiency. A singly-linked list may only be traversed in the forward
direction. Singly-linked lists are ideal for applications with large
datasets and few or no removals or for implementing a LIFO queue.
A list is headed by a single forward pointer (or an array of forward
pointers for a hash table header). The elements are doubly linked
so that an arbitrary element can be removed without a need to
traverse the list. New elements can be added to the list before
or after an existing element or at the head of the list. A list
may only be traversed in the forward direction.
A simple queue is headed by a pair of pointers, one the head of the
list and the other to the tail of the list. The elements are singly
linked to save space, so elements can only be removed from the
head of the list. New elements can be added to the list after
an existing element, at the head of the list, or at the end of the
list. A simple queue may only be traversed in the forward direction.
A tail queue is headed by a pair of pointers, one to the head of the
list and the other to the tail of the list. The elements are doubly
linked so that an arbitrary element can be removed without a need to
traverse the list. New elements can be added to the list before or
after an existing element, at the head of the list, or at the end of
the list. A tail queue may be traversed in either direction.
A circle queue is headed by a pair of pointers, one to the head of the
list and the other to the tail of the list. The elements are doubly
linked so that an arbitrary element can be removed without a need to
traverse the list. New elements can be added to the list before or after
an existing element, at the head of the list, or at the end of the list.
A circle queue may be traversed in either direction, but has a more
complex end of list detection.
簡單來說,即是單鏈表,雙鏈表,單鏈隊列,雙向隊列(尾隊列)和雙向循環隊列。
雖然這是LINUX/UNIX里面的文件,但此文件本身沒有用到LINUX/UNIX的系統特性,因而可以跨平台使用。queue.h下載。
下面對各數據結構簡單描述之。
單鏈表(singly-linked list)
singly-linked list就是一單鏈表。
singly-linked list相關的定義:
宏定義 | 說明 |
SLIST_HEAD(name, type) | 定義表頭結點。 name: 表頭結點名。 type: 結點類型。 |
SLIST_HEAD_INITIALIZER(head) | 初始化頭結點。 head: 表頭結點。 |
SLIST_ENTRY(type) | 定義鏈表的鏈域。 type: 結點類型。 |
singly-linked list函數:
宏定義 | 說明 |
SLIST_INIT(head) | 初始化頭結點。 head: 表頭結點。 |
SLIST_INSERT_AFTER(slistelm, elm, field) | 將結點elm插入到結點slistelm后面。 slistelm:鏈表中某結點。 elm:要插入的結點。 field:鏈表中鏈域的名稱。 |
SLIST_INSERT_HEAD(head, elm, field) | 將結點elm插入到頭結點head后面。 head: 表頭結點。 elm:要插入的結點。 field:鏈表中鏈域的名稱。 |
SLIST_REMOVE_HEAD(head, field) | 移除將表頭結點下面一個結點。 head: 表頭結點。 field:鏈表中鏈域的名稱。 |
SLIST_REMOVE(head, elm, type, field) | 移除將elm結點,elm結點一定要是鏈表中一結點。 head: 表頭結點。 elm:某結點。 type: 結點類型。 field:鏈表中鏈域的名稱。 |
SLIST_FOREACH(var, head, field) | 遍歷鏈表,相當於for循環。 var: 結點類型的變量名稱。 head: 表頭結點。 field:鏈表中鏈域的名稱。 |
singly-linked list 訪問方法:
宏定義 | 說明 |
SLIST_EMPTY(head) | 判斷鏈表是否為空。 head: 表頭結點。 |
SLIST_FIRST(head) | 訪問鏈表里的第一個元素。 head: 表頭結點。 |
SLIST_NEXT(elm, field) | 訪問elm結點后一個元素。 elm:某結點。 field:鏈表中鏈域的名稱。 |
簡單例子:
struct SListItem { int data; SLIST_ENTRY(SListItem) entry; }; /* struct SListItem { int data; struct { struct SListItem* sle_next; } entry; } */ void slist_demo() { struct SListItem* item = NULL; SLIST_HEAD(SListHead, SListItem) shead; /* struct SListHead { struct SListItem* slh_first; } shead; */ SLIST_INIT(&shead); item = (struct SListItem*)malloc(sizeof(struct SListItem)); item->data = 1; SLIST_INSERT_HEAD(&shead, item, entry); /* item->entry.sle_next = (&shead)->slh_first; (&shead)->slh_first = item; */ item = (struct SListItem*)malloc(sizeof(struct SListItem)); item->data = 2; SLIST_INSERT_HEAD(&shead, item, entry); /* item->entry.sle_next = (&shead)->slh_first; (&shead)->slh_first = item; */ SLIST_FOREACH(item, &shead, entry){ printf("%d ", item->data); } /* for(item = (&shead)->slh_first; item; item = item->entry.sle_next){ ... } */ printf("\n"); while(!SLIST_EMPTY(&shead)){ item = SLIST_FIRST(&shead); printf("remove %d\n", item->data); SLIST_REMOVE(&shead, item, SListItem, entry); free(item); } /* while(!((&shead)->slh_first == NULL)){ item = (&shead)->slh_first; ... (&shead)->slh_first = (&shead)->slh_first->entry.sle_next; ... } */ } /*結果 2 1 remove 2 remove 1 */
雙向鏈表(list)
list就是雙向鏈表,不過鏈域有點古怪,指向前一個結點是指針的指針。
list 相關定義
宏定義 | 說明 |
LIST_HEAD(name, type) | 定義表頭結點。 name: 表頭結點名。 type: 結點類型。 |
LIST_HEAD_INITIALIZER(head) | 初始化頭結點。 head: 表頭結點。 |
LIST_ENTRY(type) | 定義鏈表的鏈域。 type: 結點類型。 |
list函數
宏定義 | 說明 |
LIST_INIT(head) | 初始化頭結點。 head: 表頭結點。 |
LIST_INSERT_AFTER(listelm, elm, field) | 將結點elm插入到結點listelm后面。 listelm:鏈表中某結點。 elm:要插入的結點。 field:鏈表中鏈域的名稱。 |
LIST_INSERT_BEFORE(listelm, elm, field) | 將結點elm插入到結點listelm前面。 listelm:鏈表中某結點。 elm:要插入的結點。 field:鏈表中鏈域的名稱。 |
LIST_INSERT_HEAD(head, elm, field) | 將結點elm插入到頭結點head后面。 head: 表頭結點。 elm:要插入的結點。 field:鏈表中鏈域的名稱。 |
LIST_REMOVE(elm, field) | 移除將elm結點。 elm:某結點。 field:鏈表中鏈域的名稱。 |
LIST_FOREACH(var, head, field) | 遍歷鏈表,相當於for循環。 var: 結點類型的變量名稱。 head: 表頭結點。 field:鏈表中鏈域的名稱。 |
list訪問方法
宏定義 | 說明 |
LIST_EMPTY(head) | 判斷鏈表是否為空。 head: 表頭結點。 |
LIST_FIRST(head) | 訪問鏈表里的第一個元素。 head: 表頭結點。 |
LIST_NEXT(elm, field) | 訪問elm結點后一個元素。 elm:某結點。 field:鏈表中鏈域的名稱。 |
注意,因為list是雙向鏈表,但在訪問方法里沒有寫出訪問前一個元素的宏。因而可以這樣寫一個,參數含義和LIST_NEXT一樣:
#define LIST_PRE(elm, field) \
(((elm)->field.le_pre) != &elm ? *((elm)->field.le_pre) : NULL)
簡單例子:
struct ListItem { int data; LIST_ENTRY(ListItem) entry; }; /* struct ListItem { int data; struct{ struct ListItem* le_next; struct ListItem** le_prev; } entry; }; */ void list_demo() { struct ListItem* item = NULL; LIST_HEAD(ListHead, ListItem) lhead; /* struct ListHead { struct ListItem* lh_first; } lhead; */ LIST_INIT(&lhead); /* do{ (&lhead)->lh_first = NULL; }while(0); */ item = (struct ListItem*)malloc(sizeof(struct ListItem)); item->data = 1; LIST_INSERT_HEAD(&lhead, item, entry); item = (struct ListItem*)malloc(sizeof(struct ListItem)); item->data = 2; LIST_INSERT_HEAD(&lhead, item, entry); /* do{ if(((item)->entry.le_next = (&lhead)->lh_first) != NULL) (&lhead)->lh_first->entry.le_pre = &(elm)->entry.le_next; (&lhead)->lh_first = (item); (item)->entry.le_prev = &(&lhead)->lh_first; }while(0); */ LIST_FOREACH(item, &lhead, entry){ printf("%d ", item->data); } /* for ((item) = ((&lhead)->lh_first); (item); (item) = ((item)->entry.le_next)){ ... } */ printf("\n"); while(!LIST_EMPTY(&lhead)){ item = LIST_FIRST(&lhead); printf("remove %d\n", item->data); LIST_REMOVE(item, entry); free(item); } /* while(!((&lhead)->lh_first == NULL)){ item = ((&lhead)->lh_first); ... do{ if ((item)->entry.le_next != NULL) \ (item)->entry.le_next->entry.le_prev = \ (item)->entry.le_prev; \ *(item)->entry.le_prev = (item)->entry.le_next; \ } while (0); ... } */ } /* 結果 2 1 remove 2 remove 1 */
簡單隊列(simple queue)
簡單來說,就是表對有兩個鏈域,分別指向頭和尾。
simple queue 定義(具體說明不再寫,可以參考list的,或者就直接展開宏)
宏定義 | 說明 |
SIMPLEQ_HEAD(name, type) | |
SIMPLEQ_HEAD_INITIALIZER(head) | |
SIMPLEQ_ENTRY(type) |
simple queue函數(具體說明不再寫,可以參考list的,或者就直接展開宏)
宏定義 | 說明 |
SIMPLEQ_INIT(head) | |
SIMPLEQ_INSERT_HEAD(head, elm, field) | |
SIMPLEQ_INSERT_TAIL(head, elm, field) | |
SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) | |
SIMPLEQ_REMOVE_HEAD(head, field) | |
SIMPLEQ_REMOVE(head, elm, type, field) | |
SIMPLEQ_FOREACH(var, head, field) |
simple queue方法(具體說明不再寫,可以參考list的,或者就直接展開宏)
宏定義 | 說明 |
SIMPLEQ_EMPTY(head) | |
SIMPLEQ_FIRST(head) | |
SIMPLEQ_NEXT(elm, field) |
簡單例子:
用法與list用法類似,不再重復。
單鏈尾隊列(singled-linked tail queue)
這個和Simple queue是一樣的,參考simple queue
singled-linked tail queue定義(具體說明不再寫,可以參考list的,或者就直接展開宏)
宏定義 | 說明 |
STAILQ_HEAD(name, type) | |
STAILQ_HEAD_INITIALIZER(head) | |
STAILQ_ENTRY(type) |
tail queue 函數(具體說明不再寫,可以參考list的,或者就直接展開宏)
宏定義 | 說明 |
STAILQ_INIT(head) | |
STAILQ_INSERT_HEAD(head, elm, field) | |
STAILQ_INSERT_TAIL(head, elm, field) | |
STAILQ_INSERT_AFTER(head, listelm, elm, field) | |
STAILQ_REMOVE_HEAD(head, field) | |
STAILQ_REMOVE(head, elm, type, field) | |
STAILQ_FOREACH(var, head, field) |
tail queue方法(具體說明不再寫,可以參考list的,或者就直接展開宏)
宏定義 | 說明 |
STAILQ_EMPTY(head) | |
STAILQ_FIRST(head) | |
STAILQ_NEXT(elm, field) |
簡單例子:
用法與list用法類似,不再重復。
循環隊列(circle queue)
循環隊列。
circle queue定義(具體說明不再寫,可以參考list的,或者就直接展開宏)
宏定義 | 說明 |
LIST_HEAD(name, type) | |
LIST_HEAD_INITIALIZER(head) | |
LIST_ENTRY(type) |
circle queue函數(具體說明不再寫,可以參考list的,或者就直接展開宏)
宏定義 | 說明 |
LIST_INIT(head) | |
LIST_INSERT_AFTER(listelm, elm, field) | |
LIST_INSERT_BEFORE(listelm, elm, field) | |
LIST_INSERT_HEAD(head, elm, field) | |
LIST_REMOVE(elm, field) | |
LIST_FOREACH(var, head, field) |
circle queue訪問方法(具體說明不再寫,可以參考list的,或者就直接展開宏)
宏定義 | 說明 |
LIST_EMPTY(head) | |
LIST_FIRST(head) | |
LIST_NEXT(elm, field) |
簡單例子
用法與list用法類似,不再重復。
小結
雖然這是linux/unix實現的經過長時間考驗的成熟的數據結構,但是如果不是很熟悉的話,第一次用起來還是感覺挺不習慣的。但是好在各個數據結構的定義和方法都非常類似,接口比較統一,如果用多的了,熟悉了,感覺就不錯了。