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

C 語言實作靜態鏈表與動態鏈表:核心差異與完整代碼對比

     編輯:關於C語言

核心概念對比

維度靜態鏈表動態鏈表
節點記憶體編譯期靜態分配(全域/棧上)執行期 malloc 動態分配(堆上)
最大長度固定,受陣列大小限制理論上僅受記憶體限制
插入/刪除僅修改指標,無需移動資料同,僅修改指標
記憶體釋放不支援單節點釋放,生命週期隨程式支援 free 精確釋放
適用場景嵌入式、節點數固定、禁用動態分配環境通用場景、節點數動態變化

靜態鏈表完整實作

#include 
#include 

#define MAX_NODES 100

typedef struct Node {
    int id;
    float score;
    int next;  // 使用陣列索引作為「指標」,-1 表示空
} Node;

Node pool[MAX_NODES];
int head = -1;
int free_list = 0; // 閒置節點鏈表頭

void init_pool() {
    for (int i = 0; i < MAX_NODES - 1; ++i) pool[i].next = i + 1;
    pool[MAX_NODES - 1].next = -1;
}

int alloc_node() {
    if (free_list == -1) return -1;
    int idx = free_list;
    free_list = pool[idx].next;
    return idx;
}

void free_node(int idx) {
    pool[idx].next = free_list;
    free_list = idx;
}

void insert_after(int prev_idx, int id, float score) {
    int idx = alloc_node();
    if (idx == -1) { puts("Pool full"); return; }
    pool[idx].id = id;
    pool[idx].score = score;
    pool[idx].next = pool[prev_idx].next;
    pool[prev_idx].next = idx;
}

void traverse(int head_idx) {
    for (int p = head_idx; p != -1; p = pool[p].next) {
        printf("id=%d score=%.1f\n", pool[p].id, pool[p].score);
    }
}

int main() {
    init_pool();
    head = alloc_node(); pool[head].id = 1; pool[head].score = 80.0; pool[head].next = -1;
    insert_after(head, 2, 85.0);
    insert_after(pool[head].next, 3, 90.0);
    traverse(head);
    return 0;
}

動態鏈表完整實作

#include 
#include 

typedef struct DNode {
    int id;
    struct DNode* next;
} DNode;

DNode* create_node(int id) {
    DNode* n = (DNode*)malloc(sizeof(DNode));
    if (!n) return NULL;
    n->id = id;
    n->next = NULL;
    return n;
}

void append(DNode** head, int id) {
    DNode* n = create_node(id);
    if (!n) return;
    if (!*head) { *head = n; return; }
    DNode* p = *head;
    while (p->next) p = p->next;
    p->next = n;
}

void traverse_dyn(DNode* head) {
    for (DNode* p = head; p; p = p->next) printf("%d ", p->id);
    puts("");
}

void destroy(DNode** head) {
    DNode* p = *head;
    while (p) {
        DNode* next = p->next;
        free(p);
        p = next;
    }
    *head = NULL;
}

int main() {
    DNode* head = NULL;
    for (int i = 1; i <= 5; ++i) append(&head, i);
    traverse_dyn(head);
    destroy(&head);
    return 0;
}

進階對比:效能與工程考量

記憶體佈局

  • 靜態鏈表:節點連續分佈於陣列,CPU 快取命中率高,遍歷極快。
  • 動態鏈表:節點散佈於堆,快取未命中多,遍歷較慢。

記憶體碎片

  • 靜態鏈表:零碎片,但可能有內部碎片(陣列未滿)。
  • 動態鏈表:頻繁 malloc/free 產生外部碎片,長期運行需記憶體池優化。

執行緒安全

  • 靜態池需外部鎖保護 free_list。
  • 動態分配器通常執行緒安全,但鏈表操作本身非原子,仍需鎖。

工程選型建議

  1. 嵌入式/實時系統:優先靜態鏈表(決定性記憶體、無分配失敗風險)。
  2. 通用應用/節點數不定:動態鏈表 + 記憶體池(jemalloc/tcmalloc 或自建池)。
  3. 高效能遍歷:考慮陣列基礎的 std::vector / 動態陣列,鏈表非最佳選擇。
  4. LRU 快取、檔案系統 inode:雙向鏈表(動態/靜態均可)+ 雜湊表。

總結

靜態鏈表以陣列索引模擬指標,犧牲靈活性換取確定性與快取友好;動態鏈表以堆分配換取無上限擴展性。理解兩者本質差異,結合具體約束(記憶體上限、實時性、碎片容忍度)選型,是系統級程式設計師的基本功。

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