C11 引入关键字 _Generic,在编译期根据控制表达式的类型从候选列表中选择对应表达式,常用于宏中实现类型分派,在语法上接近 C++ 的函数重载,但与纯文本宏、类型别名的适用场景不同。
_Generic 表达式
_Generic 根据控制表达式的类型匹配关联类型,返回所选分支的表达式结果;控制表达式与未选中分支均不求值。
_Generic( (expression),
type_1 : statements,
type_2 : statements,
...
default : statements
)按类型选择整型常量
#include <stdio.h>
int main(void) {
printf("%d\n", _Generic(1.0L, float : 1, double : 2,
long double : 3, default : 0));
printf("%d\n", _Generic(1L, float : 1, double : 2,
long double : 3, default : 0));
printf("%d\n", _Generic(1.0L, float : 1, double : 2,
long double : 3));
return 0;
}输出:
3
0
31.0L 的类型为 long double;1L 为 long,无对应项时走 default。
在宏中实现类型相关行为
#include <stdio.h>
#define typeName(T) _Generic((T), \
char* : "String", \
int : "Integer", \
long : "Long Integer", \
default : "Others")
int main(void) {
printf("%s\n", typeName("A"));
printf("%s\n", typeName(5));
printf("%s", typeName(5.12));
return 0;
}输出:
String
Integer
Others类型名称与数学函数分派
#include <stdio.h>
#define TYPE_NAME(X) _Generic((X), \
int: "int", \
char: "char", \
double: "double", \
default: "unknown")
int main(void) {
printf("Type name: %s\n", TYPE_NAME(42));
printf("Type name: %s\n", TYPE_NAME(42.42));
printf("Type name: %s\n", TYPE_NAME('c'));
return 0;
}字符常量 'c' 在 C 中的类型为 int,故第三行输出 int。
标准头文件 tgmath.h 利用 _Generic 将泛型数学函数名映射到 float、double 等具体函数:
#define cbrt(X) _Generic((X), \
long double: cbrtl, \
default: cbrt, \
float: cbrtf \
)(X)
#include <math.h>
#include <stdio.h>
int main(void) {
double x = 8.0;
const float y = 3.375;
printf("cbrt(8.0) = %f\n", cbrt(x));
printf("cbrtf(3.375) = %f\n", cbrt(y));
return 0;
}输出:
cbrt(8.0) = 2.000000
cbrtf(3.375) = 1.500000宏体末尾的 (X) 对所选函数指针进行调用,实参仅在最终调用时求值。
规则与限制
- 关联列表中同一类型不得重复出现。
- 不得同时列出互相兼容的类型(如枚举与其底层整型)。
- 无
default时,控制表达式类型须与某一关联类型完全匹配。 - 匹配前会丢弃
const、volatile等限定,并应用左值转换规则。
优缺点
- 在宏层实现类型安全分派,减少手写多份函数。
- 语法较复杂,错误信息可能难以阅读;宜用于库级抽象而非滥用。
本节说明了 _Generic 的语义及与宏的配合方式,可作为本章进阶主题的收束;继续学习时可结合标准库头文件与变参函数、格式说明符理解类型相关的 I/O 与泛型数学接口。