首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

通用雙向鏈表實作:參考 Linux kernel list_head 設計

     編輯:關於C語言

設計動機

Linux 核心 list_head 以 typeof 與 container_of 實現極致泛型,但 typeof 為 GCC 延伸,非標準 C。本實作退而求其次:要求使用者將鏈表節點作為結構體第一個成員,以標準 C 實現型別無關的雙向鏈表。

核心資料結構

// IList.h
#ifndef _I_LIST_H_
#define _I_LIST_H_

typedef struct _IList {
    struct _IList *_prev;
    struct _IList *_next;
} IList, *PIList;

// 初始化鏈表頭(頭節點自我指向)
#define IList_Init(pList) do { (pList)->_prev = (pList)->_next = (pList); } while(0)

// 插入新節點到 pLink 之前
#define IList_Insert(pLink, pNewLink) do { \
    (pNewLink)->_prev = (pLink)->_prev; \
    (pNewLink)->_next = (pLink); \
    (pLink)->_prev->_next = (pNewLink); \
    (pLink)->_prev = (pNewLink); \
} while(0)

// 刪除節點
#define IList_Remove(pLink) do { \
    (pLink)->_prev->_next = (pLink)->_next; \
    (pLink)->_next->_prev = (pLink)->_prev; \
    (pLink)->_prev = (pLink)->_next = (pLink); \
} while(0)

// 遍歷宏(安全版,支援刪除當前節點)
#define IList_Foreach(pList, pos) \
    for (pos = (pList)->_next; pos != (pList); pos = pos->_next)

#define IList_Foreach_Safe(pList, tmp, pos) \
    for (pos = (pList)->_next, tmp = pos->_next; pos != (pList); pos = tmp, tmp = pos->_next)

// 取第 n 個節點(0-based)
PIList IList_Nth(PIList pList, int n);

#endif

實作細節

// IList.c
#include "IList.h"

PIList IList_Nth(PIList pList, int n) {
    PIList pLink = pList->_next;
    int count = 0;
    while (pLink != pList && count < n) {
        pLink = pLink->_next;
        count++;
    }
    return (pLink != pList) ? pLink : NULL;
}

使用範例:學生資料管理

// IListTest.c
#include <stdio.h>
#include <stdlib.h>
#include "IList.h"

typedef struct _Student {
    IList link;       // 必須是第一個成員
    int id;
    char name[32];
} Student;

int main(int argc, char *argv[]) {
    if (argc < 2) return 1;
    int num = atoi(argv[1]);

    IList head;
    IList_Init(&head);

    for (int i = 0; i < num; ++i) {
        Student *s = (Student*)malloc(sizeof(Student));
        s->id = i + 1;
        snprintf(s->name, sizeof(s->name), "Student_%d", s->id);
        IList_Insert(&head, &s->link); // 插入到頭部
    }

    // 遍歷輸出
    PIList pos;
    IList_Foreach(&head, pos) {
        Student *s = (Student*)pos; // 因 link 是第一成員,位址相同
        printf("ID: %d, Name: %s\n", s->id, s->name);
    }

    // 安全刪除釋放
    PIList tmp, pos2;
    IList_Foreach_Safe(&head, tmp, pos2) {
        Student *s = (Student*)pos2;
        IList_Remove(pos2);
        free(s);
    }
    return 0;
}

關鍵設計權衡

特性Linux list_head本實作
泛型機制typeof + container_of節點必須為第一成員
標準相容GCC 延伸純標準 C (C89/C99)
記憶體佈局節點可嵌入任意位置固定在結構體開頭
型別安全編譯期檢查弱同樣需手動轉型

適用場景

  • 嵌入式系統、跨編譯器專案(MSVC、Clang、GCC 混用)。
  • 需極簡、零依賴的雙向鏈表容器。
  • 教學用途:理解核心鏈表設計精髓。

雖失去 container_of 的靈活性,但以標準 C 實現核心邏輯,代碼清晰、可移植,是資源受限環境的優選方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved