You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何C语言泛型更常使用void*而非宏实现?

为什么C语言中用void*实现泛型比宏方案更常见?

先看你提供的用宏实现泛型Option类型的代码:

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#define OPTION(T)                                                              \
  struct option_##T {                                                          \
    bool exists;                                                               \
    T data;                                                                    \
  };                                                                           \
  typedef struct option_##T option_##T;                                        \
  static inline bool is_some_##T(option_##T x) { return x.exists; }            \
  static inline bool is_none_##T(option_##T x) { return !x.exists; }

#define is_some(x) _Generic((x), option_int: is_some_int, option_char: is_some_char)(x)
#define is_none(x) _Generic((x), option_int: is_none_int, option_char: is_none_char)(x)

OPTION(int);
OPTION(char);

int main(int argc, char *argv[]) {
  option_int x = {.exists = true, .data=3};
  option_char y = {.exists = true, .data='a'};
  
  printf("x is some: %d\n", is_some(x));
  printf("y is some: %d\n", is_some(y));
  
  return 0;
}

对比之下,void*方案更常见主要有这些原因:

  • 避免代码膨胀
    宏会为每个实例化的类型生成一套独立的结构体和函数,哪怕逻辑完全一致。如果用OPTION实例化十几种类型,二进制里会多出大量重复代码,体积直接飙升。而void*方案只需要一套代码,所有类型共用,二进制体积紧凑得多。

  • 通用性更强,无需提前绑定类型
    void的泛型组件可以直接处理任意类型的数据,不用提前用宏预编译。比如一个基于void的链表,直接就能存int、char、自定义结构体的指针,不用为每种类型单独生成链表代码。宏方案必须提前知道要支持的类型,每次加新类型都要重新实例化宏,灵活性差很多。

  • 接口统一,维护成本低
    void*方案对外提供的是统一接口,比如option_is_some(Option* opt),不管存的是int还是自定义类型,调用方式完全一致。宏方案的接口会随类型变化,哪怕用_Generic封装,新增类型时也要修改_Generic的分支,越到后面维护越麻烦。

  • 支持动态类型场景
    如果需要处理动态类型(比如从文件读取不同类型的数据、做通用配置解析),void*方案可以配合类型标签(比如在结构体里加个enum标记数据类型)实现动态判断和转换。宏方案在编译期就绑定了类型,根本没法处理这类场景。

  • 兼容性拉满
    虽然C11有_Generic,但一些老编译器(尤其是嵌入式环境的)可能不支持。而void*是C语言从早期就有的特性,几乎所有编译器都能兼容,不用担心环境适配问题。

这里也给一个void*实现Option类型的示例参考:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    bool exists;
    void* data;
} Option;

static inline bool option_is_some(Option* opt) {
    return opt != NULL && opt->exists && opt->data != NULL;
}

static inline bool option_is_none(Option* opt) {
    return !option_is_some(opt);
}

Option* option_some(void* data) {
    Option* opt = malloc(sizeof(Option));
    if (!opt) return NULL;
    opt->exists = true;
    opt->data = data;
    return opt;
}

Option* option_none(void) {
    Option* opt = malloc(sizeof(Option));
    if (!opt) return NULL;
    opt->exists = false;
    opt->data = NULL;
    return opt;
}

int main() {
    int int_val = 3;
    char char_val = 'a';
    
    Option* x = option_some(&int_val);
    Option* y = option_some(&char_val);
    
    printf("x is some: %d\n", option_is_some(x));
    printf("y is some: %d\n", option_is_some(y));
    
    free(x);
    free(y);
    return 0;
}

内容的提问来源于stack exchange,提问作者Capybara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 08:34:51