是否可以在 GCC/GNU C 中编写一个 _Static_assert 来验证编译时内存中位字段的布局?

Mac*_*dek 8 c gcc static-assert assertion bit-fields

假设我有以下定义:

#include <stdbool.h>
#include <stdint.h>
#define ASSERT(cond) _Static_assert(cond, #cond)

typedef union {
    struct {
        bool bit0:1;
        bool bit1:1;
        bool bit2:1;
        bool bit3:1;
        bool bit4:1;
        bool bit5:1;
        bool bit6:1;
        bool bit7:1;
    };
    uint8_t bits;
} byte;

ASSERT(sizeof(byte) == sizeof(uint8_t));
Run Code Online (Sandbox Code Playgroud)

能不能写个代码,比如

#include <assert.h>
// ...
    assert(((byte) { .bit0 = 1 }).bits == 0b00000001);
    assert(((byte) { .bit1 = 1 }).bits == 0b00000010);
    assert(((byte) { .bit2 = 1 }).bits == 0b00000100);
    assert(((byte) { .bit3 = 1 }).bits == 0b00001000);
    assert(((byte) { .bit4 = 1 }).bits == 0b00010000);
    assert(((byte) { .bit5 = 1 }).bits == 0b00100000);
    assert(((byte) { .bit6 = 1 }).bits == 0b01000000);
    assert(((byte) { .bit7 = 1 }).bits == 0b10000000);
// ...
Run Code Online (Sandbox Code Playgroud)

如果不满足上述条件,那会导致编译时失败吗?

(当我尝试在ASSERT宏中放置条件时,编译器会抱怨expression in static assertion is not constant,这当然很有意义)

该解决方案允许使用 C 语言的 GNU 扩展。

PSk*_*cik 6

我不认为你可以。 _Static_assert需要验证参数表达式是否满足标准 C 对整数常量表达式的要求。

有一些方法在 gcc 上有时可以将不满足这​​些要求但在编译时为优化器已知的布尔表达式转换为编译时错误或警告。

例如:

#include <assert.h>
#if  __GNUC__ && !__clang__
#define $SassertIfUCan0(X) \
    (__extension__({ /*ellicit a -Wvla-larger-than */ \
         (!__builtin_constant_p(X)) ? 0 : \
        ({ char volatile $SassertIfUCan0_[ (!__builtin_constant_p(X)||(X))  ? 1:-1]; \
         $SassertIfUCan0_[0]=0,0;}); \
        __auto_type $SassertIfUCan0 = X; \
         assert($SassertIfUCan0); \
         0; \
    }))
#endif

int main(int C, char **V)
{
    int x = 0; $SassertIfUCan0(x);
    //these also ellicit compile-time errrors:
    /*$SassertIfUCan0(C-C);*/
    /*$SassertIfUCan0(C*0);*/
}
Run Code Online (Sandbox Code Playgroud)

可以将编译时已知变量x(技术上不是整数常量)的空性转换为编译/时警告/错误 ( "-Wvla-larger-than")。

不幸的是,该宏不适用于每个表达式,其中包括基于位域的示例。

(如果表达式恰好在编译时已知且为假,我希望编译器有一种编译失败的机制。)

因此,AFAIK,您可以做的最接近的事情是编译时检测平台,其 ABI 已知可以保证您所需的位域布局:

#if __linux__ && __x86_64__
#elif 0//...
//...
#else
   #error "bitfields not known to be little-endian"
#endif
Run Code Online (Sandbox Code Playgroud)