我已经定义了一个类似的结构:
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
的值。但是,当我尝试设置一个超出范围的值时,它将被隐式截断(在运行时)。有了断言,我想检查这样的情况,不仅发生可能截断,而且实际上发生了 的情况。
答案 0 :(得分:2)
我正在寻找像
sizeofbitfieldmemberinbits()
这样的表达式 可以在编译时由编译器计算
该标准所描述的术语是“ constant expression”:
常量表达式可以在翻译过程中求值,而不是 运行时,因此可以在任何常量可以使用的地方使用 是。
(C2011,6.6 / 2)
您继续阐明使用此类常量表达式的目的:
关于断言,我想检查没有截断的情况 只是可能发生,但是实际上确实发生。
但是请注意
为此,位域的大小是次要的。您真正想要的是最大可表示值。对于带符号类型的位域,也许您也想要最小值。
您实际上不需要在表达式等正则断言中使用常量表达式(与静态断言相对)。正则断言中的表达式在运行时求值。
另一方面,某些实现可能仍会在翻译(编译)时计算一些不满足标准对常量表达式的定义的表达式。
点(2)和(3)对您来说很幸运,因为位域具有无法直接表示的第二类类型。在主机结构对象的上下文之外,没有任何位域类型的值,也没有类型名称可以用来表示位域的有效类型。并且那表示没有常数表达式可求位字段成员的位数或最大值,除非它结合了该成员的先验知识,因为结构(包括结构文字)不在其中可能会出现在合适的常量表达式中的操作数:
算术常数表达式应具有算术类型并且应 仅具有整数常量,浮点常量, 枚举常量,字符常量,
sizeof
表达式,其 结果是整数常量和_Alignof
表达式。投 算术常数表达式中的运算符只能转换 算术类型转换为算术类型,但作为操作数的一部分sizeof
或_Alignof
运算符。
毕竟,我认为问题真的可以归结为:
我不确定是否可以保证工作
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
的可能性要小得多。