我已经定义了一个结构:
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
的值。但是,当我尝试设置超出范围的值时,它将被隐式截断(在运行时)。通过断言,我想检查不仅可能发生这种截断的情况,而是实际发生的情况。
我正在寻找像
sizeofbitfieldmemberinbits()
这样的表达式,可以在构建时由编译器计算
您所描述的标准术语是“constant expression”:
可以在转换期间而不是运行时期间评估常量表达式,并且因此可以在常量可以在任何地方使用。
(C2011,6.6 / 2)
接下来,您将阐明要使用此类常量表达式的目的:
通过断言,我想检查不仅可能发生这种截断的情况,而是实际发生的情况。
但请注意
点(2)和(3)对你来说是幸运的,因为位域具有不能直接表达的第二类类型。在主机结构对象的上下文之外没有任何位域类型的值,并且没有可以表示位域的有效类型的类型名称。这意味着没有常量表达式可以计算位域成员的位数或最大值,除非它包含该成员的先验知识,因为结构(包括结构文字)不在可能出现在合适的操作数中。常数表达式:
算术常量表达式应具有算术类型,并且只能具有整数常量,浮点常量,枚举常量,字符常量,结果为整数常量的
sizeof
表达式和_Alignof
表达式的操作数。算术常量表达式中的强制运算符只能将算术类型转换为算术类型,除非作为sizeof
或_Alignof
运算符的操作数的一部分。
(Qazxswpoi)
毕竟,我认为这个问题可以归结为:
我不确定这是否可以保证:
C2011 6.6/8
对于无符号位域,例如assert(someveal <= ((mystruct){.threebits = -1}).threebits);
,它保证在C99或更高版本中工作。早期版本的C没有复合文字或指定的初始化器,但是,即使在今天,您可能遇到的一些C实现也不符合C99。在这样的实现中,您可能只需要定义一个(可能是mystruct.threebits
,也许是const
)实例,在其中记录限制...
static
...然后与其成员进行比较:
static const struct mystruct mystruct_limits = { -1, -1, -1 };
请注意,struct member初始值设定项适用于在简单赋值中应用的相同转换,因此只要成员具有无符号类型,-1s作为初始化值就可以很好地定义为具有所需效果。
另请注意,虽然assert(someveal <= mystruct_limits.threebits);
是出于此目的所希望的,但它直到C99才被标准化。不过,在C99之前这是一个非常常见的扩展,并且你不太可能遇到拒绝const
的C编译器而不是拒绝复合文字的编译器。
有没有办法确定C结构的位字段成员的保证最小大小(以位为单位)
我想检查不仅可能发生这种截断的情况,而且实际上是这样。
const
是不够的,因为它的相关代码在运行时肯定不存在。
检查字段的最大值。当字段是某些无符号类型时很容易做到。
assert()
产量
#include <inttypes.h>
#include <stdio.h>
int main(void) {
struct mystruct {
uint32_t onebyte :8;
uint32_t twobytes :16;
uint32_t threebits :3;
};
struct mystruct obj;
for (int test = 0; test < 20; test++) {
unsigned n = rand() % 10;
// I want to check for cases where such truncation not just might occur,
// but when actually does.
if (n > (struct mystruct) {.threebits = -1}.threebits) {
printf("Truncation will occur, %u\n", n);
}
obj.threebits = n;
}
return obj.threebits;
}