四时宝库

程序员的知识宝库

【C语法硬核20讲】20 编译期技巧:_Generic与断言

目标:把 _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 配合,既安全又零开销

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接