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

C語言實現泛型程式設計:函數指標與巨集兩大技法

     編輯:關於C

C 語言泛型程式設計可行性

雖無原生模板支援,C 語言仍可透過函數指標與巨集實現泛型邏輯,解決函數名稱衝突、提升代碼復用性。

方法一:函數指標實現運行期多態

核心架構


typedef int (*FXPrintFun)(void *data);  // 統一介面

int print(FXPrintFun _print, void *data) {
    return _print(data);  // 委派具體實作
}

// 具體類型處理函數
static int print_int(void *data) {
    printf("%d", (int)data);      // 直接轉換 (僅限 int 大小 ≤ 指標)
    return RET_OK;
}

static int print_int2(void *data) {
    printf("%d", *(int*)data);    // 正確:解引用指標
    return RET_OK;
}

static int print_float(void *data) {
    printf("%f", *(float*)data);
    return RET_OK;
}

static int print_str(void *data) {
    printf("%s", (char*)data);
    return RET_OK;
}

使用範例


int main() {
    int i = 0;
    float f = 2.6f;
    char *test = "Generics test!";
    int *pi = &i;
    float *pf = &f;
    
    // 方式1:值傳遞 (僅適用 int/指標等大小相同類型)
    for (; i < 10; i++)
        print(print_int, (void*)i);
    
    // 方式2:指標傳遞 (通用安全做法)
    for (i = 0; i < 10; i++)
        print(print_int2, (void*)pi);
    
    print(print_float, (void*)pf);
    print(print_str, (void*)test);
}

關鍵洞察:指標轉換安全性

類型大小轉 void* 安全性正確做法
int4 bytes⚠️ 僅因大小相符而「湊巧」可行傳 &int,接收端轉 int*
float4 bytes❌ GCC 拒絕直接轉換傳 &float,接收端轉 float*
double8 bytes❌ 64位系統指標 8 bytes 但語義錯誤傳 &double,接收端轉 double*
結構體不定❌ 嚴格禁止傳指標,接收端轉結構體指標

鐵律:永遠傳遞位址,接收端轉型後解引用。

方法二:巨集實現編譯期程式碼生成

巨集定義


// printtest.h
#ifndef __PRINTTEST_H__
#define __PRINTTEST_H__

#define GNERIC_PRINT(TYPE, FORMAT, SUFFIX) \
    int print##SUFFIX(TYPE data) { \
        printf(FORMAT, data); \
        return 0; \
    }

#endif

實例化具體函數


// printtest.c
#include "printtest.h"

GNERIC_PRINT(int, "%d", _int)
GNERIC_PRINT(float, "%f", _float)
GNERIC_PRINT(char*, "%s", _str)

int main() {
    print_int(42);
    print_float(3.14f);
    print_str("Hello");
}

巨集運算子關鍵

  • ## 符號連接運算子:print##_int → print_int
  • # 字串化運算子:#VALUE → "VALUE"

兩種方法對比

維度函數指標巨集生成
執行效能間接呼叫開銷零開銷,內聯展開
程式碼膨脹無每種類型生成獨立函數
型別安全運行期檢查編譯期檢查
除錯難度容易困難 (展開後行號偏移)
適用場景動態派發、外掛架構效能關鍵、類型固定集合

工程建議

  1. 優先使用函數指標:維護性優、除錯易、二進位體積小
  2. 巨集限於效能熱點:如排序比較函數、數學運算內循環
  3. 結合 \_Generic (C11):編譯期根據型別選擇函數,兼具安全與效能
    
    #define print(x) _Generic((x), \
        int: print_int, \
        float: print_float, \
        char*: print_str \
    )(x)
    
  4. 避免巨集巢狀過深:超過 3 層極難維護,建議改用代碼生成腳本

建置系統整合


# Makefile 觀察預處理/組譯/連結過程
OBJS = printtest.o
TARGET = printtest
SRC = printtest.c

all: $(OBJS)
    gcc -g $(OBJS) -o $(TARGET)

$(OBJS): printtest.s
    gcc -g -c printtest.s -o $(OBJS)

printtest.s: printtest.i
    gcc -g -S printtest.i -o printtest.s

printtest.i: $(SRC)
    gcc -g -E $(SRC) -o printtest.i

# 直接編譯
gcc printtest.c -o printtest
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved