鏈結串列:外包物件版 — 嵌入式的鏈結串列,是模仿 Linux 核心的作法。

本範例的實作靈感來自 Linux 核心中的雙向鏈結串列,為了簡單起見,我們改為單向鏈結串列,以便讓讀者容易理解。

程式範例:

檔案:LinkedListEmbed.c

#include <stdio.h>

#define offsetof(type, member) ((size_t) &((type *)0)->member)
#define ListEntry(ptr,type,member) ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
#define ListNew(head) ((head)->next=NULL)
#define ListAdd(head, node) { (node)->next=(head)->next; (head)->next = (node); }
#define ListEach(head, pos) for (pos = (head)->next; pos != NULL; pos = pos->next)

typedef struct listnode { 
  struct listnode *next;
} ListNode;

typedef struct {
    char *name;
    int age;
    ListNode node;
} Person;

void PersonListPrint(ListNode *head) {
  ListNode *ptr;
  ListEach(head, ptr) {
    Person *person = ListEntry(ptr, Person, node);
    printf("%s is %d years old\n", person->name, person->age);
  }
}

// 請注意,在本程式中,ListEach 會忽略表頭節點,因此 head 不應該包在 Person 裡面。
int main() {
    ListNode head;
    Person john = {
      .name = "John",
      .age = 40,
    };
    Person mary = {
      .name = "Mary",
      .age = 32,
    };
    Person george = {
      .name = "George",
      .age = 26,
    };
    ListNew(&head);
    ListAdd(&head, &john.node);
    ListAdd(&head, &mary.node);
    ListAdd(&head, &george.node);
    PersonListPrint(&head);
    return 0;
}

執行結果

D:\cp>gcc LinkedListEmbed.c -o LinkedListEmbed

D:\cp>LinkedListEmbed
George is 26 years old
Mary is 32 years old
John is 40 years old
  • 來自 jserv 的建議

    => 內文沒提到將資料搬出 list 結構的優勢,建議提供 for_each 的使用案例:

http://stackoverflow.com/questions/15754236/how-do-i-use-the-list-for-each-macro-in-list-h-from-the-linux-kernel-properly 對於《將資料搬出 list 結構的優勢》這個問題,除了巨集展開不需要進函數, 速度可能會比較快之外,我也想不出其他的優勢了,所以就請大家自己想想,或者問 jserv 。