目标:把 _Static_assert(静态断言) 与 _Generic(泛型选择) 练到熟:类型安全宏、编译期守护、类型选择分发、数组大小校验、printf 族与数值函数的类型重载。全部给出可直接复用的宏库。
1)_Static_assert:让 Bug 卡在编译期
/* 结构尺寸/偏移守护 */
struct Rec { int id; short len; char tag; };
_Static_assert(sizeof(struct Rec) == 8, "Rec size changed?");
_Static_assert(offsetof(struct Rec, len) == 4, "len offset");
/* 枚举范围守护 */
enum { BUF_CAP = 256 };
_Static_assert(BUF_CAP <= 1024, "cap too large");
- 出现位置:文件作用域与块内均可。
- 好处:ABI、数组上限、平台假设一旦被破坏立刻编译错误。
2)_Generic:按实参类型分发
A. 数值函数重载(float/double/long double)
#include <math.h>
#define tg_sqrt(x) \
_Generic((x), \
long double: sqrtl, \
double: sqrt, \
float: sqrtf \
)(x)
double a = tg_sqrt(9.0); // sqrt
float b = tg_sqrt(9.0f); // sqrtf
B. printf 助攻(选择正确格式)
#include <inttypes.h>
#define tg_pri_u(x) _Generic((x), \
unsigned: "%u", \
unsigned long: "%lu", \
uint64_t: "%" PRIu64, \
default: "%zu" /* size_t 等 */)
#define print_any_u(x) printf(tg_pri_u(x), (x))
C. “类型感知”最小宏库
#define is_float_t(x) _Generic((x), float:1, double:1, long double:1, default:0)
#define MIN_T(a,b) \
_Generic(((a)+(b)), \
long double: fminl, double: fmin, float: fminf, \
default: ((a)<(b)?(a):(b)) \
)(a,b)
说明:_Generic 做编译期选择,运行时零开销。
3)数组元素个数:编译期校验
/* 仅在“数组定义处”合法;传指针会在编译期报错 */
#define COUNT_OF(a) \
(sizeof(a)/sizeof((a)[0]) + \
0* _Generic(&(a), const void *: 0, volatile void *: 0, default: 0))
4)安全的 container_of(偏移取父)
/* 标准只提供 offsetof,container_of 为惯用宏,注意类型匹配 */
#define container_of(ptr, T, member) \
((T*)((char*)(ptr) - offsetof(T, member)))
仅当 ptr 确实是 T.member 的地址时才合法;对象需存活且对齐满足。
5)编译期选择常量与特性
#if __STDC_VERSION__ >= 201112L
#define HAVE_C11 1
#else
#define HAVE_C11 0
#endif
#if HAVE_C11
_Static_assert(sizeof(void*) >= 4, "32-bit or more");
#endif
6)把 _Generic 与 static inline 配合
- 用 _Generic 选择正确实现函数,再调 static inline 版本:既类型安全又可内联。
static inline float lerp_f(float a,float b,float t){ return a+(b-a)*t; }
static inline double lerp_d(double a,double b,double t){ return a+(b-a)*t; }
#define tg_lerp(a,b,t) _Generic(((a)+(b)+(t)), \
double: lerp_d, float: lerp_f)(a,b,t)
7)Checklist
- ABI/尺寸/上限用 _Static_assert 固定
- _Generic 做类型分发(数学函数/格式化)
- COUNT_OF 只在数组定义处使用
- container_of 仅在类型/对齐合法时使用
- 与 static inline 配合,既安全又零开销