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

一步步演算法:通用資料結構設計——以函式指標實現型別抽象

     編輯:關於C語言

C 語言無模板、無虛函式,若要寫出像 C++ STL 容器一樣支援任意型別的通用資料結構,核心技術是 void* 指標 + 函式指標。本文以循環佇列為例,示範如何將比較、列印、查找等型別相關操作抽象為回調函式,實現真正的型別無關容器。

通用佇列結構定義


typedef struct Queue {
    int front, rear;        // 隊頭、隊尾索引
    int capacity, size;     // 容量、當前元素數
    void **data;            // 元素指標陣列(void* 陣列)
    
    // 型別相關操作回調
    int (*compare)(const void*, const void*);  // 比較:<0, =0, >0
    void (*print)(const void*);                // 列印單一元素
    void* (*find_key)(const void*, const void*); // 查找:回傳匹配元素指標
    void (*free_elem)(void*);                  // 釋放元素記憶體(可選)
} Queue;

佇列建立與銷毀


Queue* queue_create(int capacity,
                    int (*cmp)(const void*, const void*),
                    void (*prn)(const void*),
                    void* (*fnd)(const void*, const void*),
                    void (*fre)(void*)) {
    if (capacity <= 0) return NULL;
    Queue *q = (Queue*)malloc(sizeof(Queue));
    if (!q) return NULL;
    q->data = (void**)malloc(capacity * sizeof(void*));
    if (!q->data) { free(q); return NULL; }
    
    q->front = q->rear = q->size = 0;
    q->capacity = capacity;
    q->compare = cmp;
    q->print = prn;
    q->find_key = fnd;
    q->free_elem = fre;
    return q;
}

void queue_destroy(Queue *q) {
    if (!q) return;
    if (q->free_elem) {
        for (int i = 0; i < q->size; i++) {
            int idx = (q->front + i) % q->capacity;
            q->free_elem(q->data[idx]);
        }
    }
    free(q->data);
    free(q);
}

核心操作實作

入隊


int queue_push(Queue *q, void *elem) {
    if (!q || q->size == q->capacity) return -1;  // 滿
    q->data[q->rear] = elem;
    q->rear = (q->rear + 1) % q->capacity;
    q->size++;
    return 0;
}

出隊


void* queue_pop(Queue *q) {
    if (!q || q->size == 0) return NULL;
    void *elem = q->data[q->front];
    q->front = (q->front + 1) % q->capacity;
    q->size--;
    return elem;  // 呼叫端負責釋放(除非佇列擁有所有權)
}

遍歷列印(原文改良版)


void queue_print(const Queue *q) {
    if (!q || q->size == 0 || !q->print) return;
    for (int i = 0; i < q->size; i++) {
        int idx = (q->front + i) % q->capacity;
        q->print(q->data[idx]);
    }
    putchar('\n');
}

查找元素


void* queue_find(const Queue *q, const void *key) {
    if (!q || !q->find_key) return NULL;
    for (int i = 0; i < q->size; i++) {
        int idx = (q->front + i) % q->capacity;
        void *found = q->find_key(q->data[idx], key);
        if (found) return found;
    }
    return NULL;
}

型別特化範例:整數佇列


int int_cmp(const void *a, const void *b) {
    int ia = *(const int*)a, ib = *(const int*)b;
    return (ia > ib) - (ia < ib);
}

void int_print(const void *a) {
    printf("%d ", *(const int*)a);
}

void* int_find(const void *elem, const void *key) {
    return (*(const int*)elem == *(const int*)key) ? (void*)elem : NULL;
}

void int_free(void *elem) { free(elem); }

// 使用示範
int main() {
    Queue *q = queue_create(10, int_cmp, int_print, int_find, int_free);
    
    for (int i = 1; i <= 5; i++) {
        int *val = (int*)malloc(sizeof(int));
        *val = i * 10;
        queue_push(q, val);
    }
    
    queue_print(q);  // 輸出: 10 20 30 40 50
    
    int key = 30;
    int *found = (int*)queue_find(q, &key);
    if (found) printf("找到: %d\n", *found);
    
    queue_destroy(q);
    return 0;
}

進階擴展方向

  1. 迭代器抽象:定義 QueueIter 結構,支援 next()、get(),算法與容器解耦
  2. 分配器抽象:加入 void* (*alloc)(size_t)、void (*dealloc)(void*),支援記憶體池
  3. 型別描述符:將回調打包成 TypeInfo 結構,建立型別註冊表,避免重複傳遞函式指標
  4. 泛型算法:實作 queue_sort、queue_filter、queue_map 等高階函式

C++ 對比啟示

C++ 模板在編譯期單態化產生型別專用代碼,零開銷;C 函式指標在執行期間接呼叫,有微小效能成本,但二進位碼體積更小、編譯更快、支援動態載入型別。兩者各有取捨,理解其本質差異有助於架構決策。

總結

通用資料結構的精髓在於「將變化點(型別操作)參數化」。以 void* 擦除型別、以函式指標注入行為,是 C 語言實現多態的標準模式。掌握此模式,即可構建出如 GLib、Linux 核心 linked list 等工業級通用容器庫。

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