嵌入式 C 里的编译期约束:用 _Static_assert 防止配置不一致

写嵌入式驱动时经常碰到这种情况:两个宏定义在不同文件里,必须保持一致,但没有任何机制强制它们匹配。

比如硬件驱动层定义了单次 DMA 的最大传输字节数,上层又计算了实际要传输的包大小——两个数字由不同参数决定,改一个忘了另一个,运行时才发现传输截断或花屏,而报错往往指向底层,很难找到根源。

一行代码可以解决:

_Static_assert(PACKET_MAX_SIZE <= DMA_MAX_TRANSFER_BYTES,
               "Packet size exceeds DMA limit");

放在宏定义之后、任何函数之前。只要有人改了相关配置导致不一致,编译直接失败:

error: static assertion failed: "Packet size exceeds DMA limit"

不用烧板子,不用跑代码,问题在编译期暴露。


基本语法

_Static_assert(常量表达式, "错误信息");

条件必须是编译期可求值的常量表达式,这是最重要的限制:

// ✅ 可以——编译期已知
_Static_assert(sizeof(packet_t) == 12, "Header size mismatch");
_Static_assert(BUF_SIZE % 4 == 0, "Must be 4-byte aligned");
_Static_assert(MODE_COUNT <= MAX_MODES, "Too many modes");

// ❌ 不行——运行时才知道值
int n = get_count();
_Static_assert(n < MAX, "...");  // 编译报错

标准与兼容性

  • C11 起内置关键字,无需任何头文件,GCC / Clang / MSVC 均支持
  • C++ 里等价写法是 static_assert(不带下划线)
  • <assert.h> 中的 static_assert 宏只是 _Static_assert 的别名,用于 C/C++ 共用代码
  • C99 及更早不支持,可用以下 trick 模拟:
// 利用负数数组长度会触发编译错误的特性
#define STATIC_ASSERT(cond) typedef char static_assert_[(cond) ? 1 : -1]

报错信息没有 _Static_assert 友好,但原理相同。


还能用在哪

// 结构体尺寸符合协议定义
_Static_assert(sizeof(packet_header_t) == 12, "Header size mismatch");

// 枚举数量不超出数组边界
_Static_assert(MODE_COUNT <= MAX_MODES, "Too many modes defined");

// 缓冲区满足对齐要求
_Static_assert(sizeof(dma_buf_t) % 4 == 0, "DMA buffer must be 4-byte aligned");

// 配置参数在合法范围内
_Static_assert(TASK_STACK_SIZE >= 2048, "Stack too small");

对比

方式 发现时机 运行时开销
不检查 花屏/崩溃后靠经验定位
运行时 assert 执行到那行才触发
_Static_assert 编译时,0 开销

凡是”两个地方的数字必须匹配”的场景,都值得加一个。改配置时编译器帮你兜底。

请登录后发表评论

    没有回复内容