撰寫程式時,多少都會用到一些串列、佇列等資料結構,有的程式語言會提供相關的實作,很容易就可以取用,相較之下,C language的使用者在這方面就比較麻煩。老練的C language程式設計師討生活的工具箱裡,都會有這方面的程式碼,以備不時之需。在open source的領域裡,也有不少高手提供這方面的函式庫。如果有需要搭配搜尋、排序的演算法,SimCList會是一個不錯的選擇,它的優點是使用方便,容易上手,速度也不錯,且授權方式採用極為友善的BSD license;倘若僅需要用到單純的list、stack、FIFO時,FreeBSD核心中的相關程式碼片段就可立即派上用場。其實Linux核心中也有功能相似的實作,較之前者可說各有勝場,只是Linux核心採用GPL授權,使用上需要多加斟酌。FreeBSD的List與Queue實作,位於sys/queue.h。
FreeBSD的queue.h提供了多個LIST的實作,可供不同場合應用。這些資料結構的使用方式差不多,個人倒是比較常用到其中的SLIST、LIST及TAILQ。其中, SLIST是單向連結的串列(Singly-linked List);LIST是雙向連結的串列;TAILQ是可直接存取末端元素的串列,可說是LIST的加強版。三者的使用時機與差異就不在此詳述了,請參考任何一本講述資料結構及演算法的教科書,都會有詳盡的說明,個人推薦「The Art of Computer Programming」。
與既有C結構(C struct)的整合
要讓資料結構可以變成串列的元素(an element of list),首先要在原本宣告的結構中透過queue.h的提供的巨集(marco):LIST_ENTRY(type)增添新的欄位。
struct element { int value; LIST_ENTRY(element) node; };
宣告LIST型別
接著,宣告串列的結構型別。宣告方式為:LIST_HEAD(name, type),name為LIST的型別名稱,type為節點的型別。
LIST_HEAD(sample_list, element);
宣告LIST變數
變數宣告方式如C language變數宣告方式。
... ... ... struct sample_list list1; ... ... ...
初始化串列
LIST初始化方式分為靜態初始化與動態初始化兩種。且看下面的範例。
... ... ... static struct sample_list list1 = LIST_HEAD_INITIALIZER(list1); ... ... ... int main(int argc, char** argv) { ... ... ... struct sample_list list2; LIST_INIT(&list2); ... ... ... }
將元素添加至串列前端
... ... ... LIST_INSERT_HEAD(&list1, e1, entry); ... ... ...
取得串列的第一個元素
... ... ... e = LIST_FIRST(&list1); ... ... ...
將元素插入至某個元素之前
要將e2插入至e1之前,可透過LIST_INSERT_BEFORE。
... ... ... LIST_INSERT_BEFORE(e1, e2, node); ... ... ...
將元素插入至某個元素之後
要將e2插入至e1之後,可透過LIST_INSERT_AFTER。
... ... ... LIST_INSERT_AFTER(e1, e2, node); ... ... ...
串列是否包含任何元素
... ... ... while (!LIST_EMPTY(list)) { ... ... ... } ... ... ...
透過迴圈尋訪串列
... ... ... LIST_FOREACH(e, list, entry) { printf("%d ", e->value); } ... ... ...
移除元素
使用LIST_REMOVE,可將特定元素從LIST中移除。
... ... ... LIST_REMOVE(e1, node); ... ... ...
取得串列中特定元素的下一個元素
LIST_NEXT可回傳串列中特定元素之下一個元素,若該元素位於串列尾端,則回傳NULL。
... ... ... LIST_FOREACH(e1, list, node) { if (LIST_NEXT(e1, node) != NULL) { printf("[ %2d ] -> ", e1->value); } else { printf("[ %2d ]", e1->value); } } ... ... ...
完整範例
#include <stdlib.h> #include <stdio.h> #include "queue.h" struct element { int value; LIST_ENTRY(element) node; }; LIST_HEAD(sample_list, element); static struct sample_list list1 = LIST_HEAD_INITIALIZER(list1); int main(int argc, char** argv) { int i = 0; struct element* e1 = NULL; struct element* e2 = NULL; struct sample_list* list = NULL; list = &list1; for (i = 0; i < 4; i++) { e1 = calloc(1, sizeof(*e1)); e1->value = i; LIST_INSERT_HEAD(list, e1, node); } e1 = LIST_FIRST(list); printf("The first element is %d.\n", e1->value); printf("list1: "); LIST_FOREACH(e1, list, node) { if (LIST_NEXT(e1, node) != NULL) { printf("[ %2d ] -> ", e1->value); } else { printf("[ %2d ]", e1->value); } } printf("\n"); // Remove all elements from list1 while (!LIST_EMPTY(list)) { e1 = LIST_FIRST(list); LIST_REMOVE(e1, node); free(e1); } list = malloc(sizeof(*list)); // Initialize the head of list2 LIST_INIT(list); e1 = calloc(1, sizeof(*e1)); e1->value = -2; LIST_INSERT_HEAD(list, e1, node); e2 = calloc(1, sizeof(*e2)); e2->value = -1; LIST_INSERT_BEFORE(e1, e2, node); e2 = calloc(1, sizeof(*e2)); e2->value = -3; LIST_INSERT_AFTER(e1, e2, node); printf("list2: "); LIST_FOREACH(e1, list, node) { if (LIST_NEXT(e1, node) != NULL) { printf("[ %2d ] -> ", e1->value); } else { printf("[ %2d ]", e1->value); } } printf("\n"); // Remove all elements from list2 e1 = LIST_FIRST(list); while (e1 != NULL) { e2 = LIST_NEXT(e1, node); free(e1); e1 = e2; } free(list); return 0; }
No comments:
Post a Comment