获取位域结构成员的大小

时间:2019-03-08 13:51:07

标签: c bit-fields

我已经定义了一个类似的结构:

struct mystruct {
    uint32_t onebyte   :  8;
    uint32_t twobytes  : 16;
    uint32_t threebits :  3;
};

我知道C将位字段定义为至少与指定的宽度相同,但是编译器可能会使用更多的内存(例如,在这种情况下为3 * 4字节)。但是,设置的宽度是保证的最小值,并且如果值超出相应范围,应用程序可能仍然会偶然运行。

要运行一些调试声明,我希望我的代码在设置成员值之前检查值是否超出允许范围:

assert(someval < (1 << sizeofbitfieldmemberinbits(((mystruct*)NULL)->threebits)));

对于这种特定情况,使用稍微不同的方法而不进行任何sizeof()调用可能是一个可行的解决方案,但是我不确定是否可以保证这种方法可行

assert(someveal <= ((mystruct){.threebits = -1}).threebits);

无论如何,有没有一种方法可以确定以位(或至少以字节为单位)的C结构的位域成员的保证最小大小?

我正在寻找一个类似sizeofbitfieldmemberinbits()的表达式,该表达式可以在编译时由编译器计算(例如((mystruct){.threebits = -1}).threebits可以求值为0x7)。

编辑: 正如John Bollinger指出的那样,分配给位字段成员的内存可以大于指定的位数,但是该成员永远不能保存大于(1 << #bits) - 1的值。但是,当我尝试设置一个超出范围的值时,它将被隐式截断(在运行时)。有了断言,我想检查这样的情况,不仅发生可能截断,而且实际上发生了 的情况。

1 个答案:

答案 0 :(得分:2)

  

我正在寻找像sizeofbitfieldmemberinbits()这样的表达式   可以在编译时由编译器计算

该标准所描述的术语是“ constant expression”:

  

常量表达式可以在翻译过程中求值,而不是   运行时,因此可以在任何常量可以使用的地方使用   是。

(C2011,6.6 / 2)

您继续阐明使用此类常量表达式的目的:

  

关于断言,我想检查没有截断的情况   只是可能发生,但是实际上确实发生

但是请注意

  1. 为此,位域的大小是次要的。您真正想要的是最大可表示值。对于带符号类型的位域,也许您也想要最小值。

  2. 您实际上不需要在表达式等正则断言中使用常量表达式(与静态断言相对)。正则断言中的表达式在运行时求值。

  3. 另一方面,某些实现可能仍会在翻译(编译)时计算一些不满足标准对常量表达式的定义的表达式。

点(2)和(3)对您来说很幸运,因为位域具有无法直接表示的第二类类型。在主机结构对象的上下文之外,没有任何位域类型的值,也没有类型名称可以用来表示位域的有效类型。并且表示没有常数表达式可求位字段成员的位数或最大值,除非它结合了该成员的先验知识,因为结构(包括结构文字)不在其中可能会出现在合适的常量表达式中的操作数:

  

算术常数表达式应具有算术类型并且应   仅具有整数常量,浮点常量,   枚举常量,字符常量,sizeof表达式,其   结果是整数常量和_Alignof表达式。投   算术常数表达式中的运算符只能转换   算术类型转换为算术类型,但作为操作数的一部分   sizeof_Alignof运算符。

C2011 6.6/8


毕竟,我认为问题真的可以归结为:

  

我不确定是否可以保证工作

assert(someveal <= ((mystruct){.threebits = -1}).threebits);

对于诸如mystruct.threebits之类的无符号位域,保证可以在C99或更高版本中使用。早期的C版本没有复合文字或指定的初始化程序,但是,甚至在今天您可能会遇到的某些C实现也不符合C99。在这样的实现上,您可能只需要定义一个结构实例(可能是const,也许是static),即可在其中记录限制...

static const struct mystruct mystruct_limits = { -1, -1, -1 };

...然后与它的成员进行比较:

assert(someveal <= mystruct_limits.threebits);

请注意,结构成员初始化器要经受与简单赋值相同的转换,因此,只要成员具有无符号类型,就很好地定义了-1s作为初始化器值,以产生所需的效果。

还要注意,尽管const是理想的,但直到C99才被标准化。但是,这是在C99之前相当普遍的扩展,与拒绝复合文字的C编译器相比,您遇到C编译器拒绝const的可能性要小得多。