雖無原生模板支援,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* 安全性 | 正確做法 |
|---|---|---|---|
| int | 4 bytes | ⚠️ 僅因大小相符而「湊巧」可行 | 傳 &int,接收端轉 int* |
| float | 4 bytes | ❌ GCC 拒絕直接轉換 | 傳 &float,接收端轉 float* |
| double | 8 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"| 維度 | 函數指標 | 巨集生成 |
|---|---|---|
| 執行效能 | 間接呼叫開銷 | 零開銷,內聯展開 |
| 程式碼膨脹 | 無 | 每種類型生成獨立函數 |
| 型別安全 | 運行期檢查 | 編譯期檢查 |
| 除錯難度 | 容易 | 困難 (展開後行號偏移) |
| 適用場景 | 動態派發、外掛架構 | 效能關鍵、類型固定集合 |
#define print(x) _Generic((x), \
int: print_int, \
float: print_float, \
char*: print_str \
)(x)
# 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