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

C 語言泛型編程實戰:利用 void* 與函數指針實現類型無關算法

     編輯:關於C語言

為什麼需要泛型?

C 語言無模板、無泛型關鍵字,但通過 void*(無類型指針)+ 顯式傳遞類型大小 + 函數指針回調,可實現類型無關的算法復用,效率接近專用版本。

從交換兩個 int 到通用 swap

專用版

void swap_int(int *a, int *b) {
    int tmp = *a; *a = *b; *b = tmp;
}

嘗試 void* 失敗版

// 錯誤:void* 無法解引用,編譯器不知大小
void swap_void(void *a, void *b) {
    void tmp = *a; // error: void value not ignored
    *a = *b;
    *b = tmp;
}

正確通用版:傳遞大小 + memcpy

#include <string.h>

void swap(void *a, void *b, size_t size) {
    char *tmp = malloc(size);
    memcpy(tmp, a, size);
    memcpy(a, b, size);
    memcpy(b, tmp, size);
    free(tmp);
}

// 調用
int x = 1, y = 2;
swap(&x, &y, sizeof(int));

double u = 3.14, v = 2.71;
swap(&u, &v, sizeof(double));

通用線性查找 lsearch

固定類型版

int lsearch_int(int key, int arr[], int n) {
    for (int i = 0; i < n; i++)
        if (arr[i] == key) return i;
    return -1;
}

通用版:void* + 元素大小 + 比較函數指針

void* lsearch(void *key, void *base, int n, size_t elemSize,
              int (*cmp)(const void*, const void*, size_t)) {
    for (int i = 0; i < n; i++) {
        void *elem = (char*)base + i * elemSize;
        if (cmp(key, elem, elemSize) == 0) return elem;
    }
    return NULL;
}

// int 比較器
int cmp_int(const void *a, const void *b, size_t sz) {
    return *(int*)a - *(int*)b;
}

// 調用
int arr[] = {5, 2, 9, 1, 7};
int key = 9;
int *found = lsearch(&key, arr, 5, sizeof(int), cmp_int);

關鍵點總結

技術作用注意事項
void*擦除類型,統一接口不能解引用,不能算術運算
size_t elemSize運行時獲取元素大小調用者必須正確傳遞 sizeof(T)
memcpy按字節複製內存適用於 POD 類型,含指針的結構體需深拷貝
函數指針回調將比較、複製、銷毀等語義外部化簽名固定,實現解耦
(char*)base + i*elemSize手動計算元素地址利用 char* 步長為 1 的特性

標準庫中的泛型實踐

  • qsort:void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void*, const void*))
  • bsearch:二分查找,同樣接口。
  • stdlib.h 中的 lfind、lsearch(POSIX)。

進階:類型安全的宏封裝

#define SWAP(a, b) do { \
    typeof(a) _tmp = (a); \
    (a) = (b); \
    (b) = _tmp; \
} while(0)  // GCC 擴展,編譯期類型安全

結語

C 語言泛型編程的核心是「數據與操作分離」:用 void* 擦除數據類型,用函數指針參數化操作語義,用 sizeof 保留大小信息。這種思想正是 C++ 模板、Java 泛型、Go 接口的底層邏輯雛形。

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