C语言中`:-!!`的含义解析

C语言中:-!!的含义解析

技术背景

在C语言中,:-!!常出现在像BUILD_BUG_ON_ZERO这样的宏里,其目的是在编译时检查某个表达式是否能被求值为0,若不能,则导致编译失败。在C11标准之前,这是一种实现编译时检查的常用技巧。

实现步骤

以下是对表达式sizeof(struct { int: -!!(e); })的详细解析:

  1. 计算表达式e:对表达式e进行求值。
  2. 逻辑双重否定!!(e):对e进行两次逻辑否定。若e == 0,结果为0;否则为1
  3. 数值取负-!!(e):对步骤2的结果进行数值取负。若结果为0,则仍为0;否则为-1
  4. e为0时struct{int: -!!(0);} 等价于 struct{int: 0;},即声明一个宽度为0的匿名整数位域,这种情况是合法的,编译可正常进行。
  5. e不为0时struct{int: -!!(1);} 等价于 struct{int: -1;},声明一个负宽度的位域会引发编译错误。

核心代码

BUILD_BUG_ON_ZERO示例

1
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int: -!!(e); }))

MY_COMPILETIME_ASSERT示例

1
2
3
4
5
6
#define MY_COMPILETIME_ASSERT(test)              \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)

MAKE_SURE_THIS_IS_FIVE示例

1
2
3
4
5
6
7
#define MAKE_SURE_THIS_IS_FIVE(number)                          \
do { \
extern void this_isnt_five(void) __attribute__((error( \
"I asked for five and you gave me " #number))); \
if ((number) != 5) \
this_isnt_five(); \
} while (0)

STATIC_ASSERT_PLUS示例

1
#define STATIC_ASSERT_PLUS(expr) ( (struct { int val; static_assert((expr), "optional error message"); }){0}.val )

最佳实践

在C11及以后的标准中,建议使用_Static_assert(在C++中为static_assert)来实现编译时检查,因为它是标准的、清晰的,并且能避免一些旧方法带来的问题。例如:

1
#define STATIC_ASSERT_PLUS(expr) ( (struct { int val; static_assert((expr), "optional error message"); }){0}.val )

常见问题

命名困惑

BUILD_BUG_ON_ZERO这个宏名容易让人误解,实际上当输入表达式的值不为0时,构建才会失败。

未定义行为

在C或GNU C中,一个没有命名成员的结构体的行为是未定义的。例如struct { int: -!!(e); } 可能会导致未定义行为,尽管GCC允许一些扩展,但这种用法仍然缺乏明确的文档支持。

替代方案的限制

虽然有像使用未定义函数、GCC的error函数属性和语句表达式等替代方法,但它们存在一些限制,如不能作为常量初始化器、不能在函数体外使用等。


C语言中`:-!!`的含义解析
https://119291.xyz/posts/c-language-colon-double-bang-meaning-analysis/
作者
ww
发布于
2025年5月26日
许可协议