我只想了解以下结构声明。哪一个更适合用于内存分配,为什么?在 unsigned char 和 unsigned int 的情况下填充呢?
struct data{
unsigned char a:3;
unsigned char b:4;
};
和
struct data{
unsigned int a:3;
unsigned int b:4;
};
我只想了解以下结构声明。哪一个更适合用于内存分配,为什么?在 unsigned char 和 unsigned int 的情况下填充呢?
struct data{
unsigned char a:3;
unsigned char b:4;
};
和
struct data{
unsigned int a:3;
unsigned int b:4;
};
位域应该用 type signed int
,来声明unsigned int
。可能支持也可能不支持其他类型。
从爱特梅尔
in the C Standard, only “unsigned (int)” and “int” are acceptable datatypes for a bitfield member. Some compilers allow “unsigned char” .......
在 c99 标准上(§6.7.2.1 #4)
位域的类型应为_Bool、signed int、unsigned int或其他一些实现定义的类型的合格或非合格版本。
如果使用的实际类型说明符是int
或typedef-name
定义为int
,则位域是signed
或是实现定义的unsigned
。
(§6.7.2.1 #15)
在结构或联合的末尾可能有未命名的填充。
实现可以分配任何大到足以容纳位域的可寻址存储单元。
进一步(§6.7.2.1 #11)
没有声明符但只有冒号和宽度的位域声明表示未命名的位域。作为一种特殊情况,宽度为 0 的位域结构成员表示不再将位域打包到放置前一个位域(如果有的话)的单元中。
未命名的位域结构成员可用于填充以符合外部强加的布局。
正如 Amogh(坚定地)和 PHIfounder 指出的那样,唯一完全可移植的类型是_Bool
,signed int
和unsigned int
. 然而,许多编译器允许其他整数类型在打包位字段时摆弄。在实践中,位域通常用于表示设备寄存器,通常每个位或一组位都有自己的含义。位的打包由C 标准的6.7.2.1 ad 11规定
实现可以分配任何大到足以容纳位字段的可寻址存储单元。如果有足够的空间剩余,紧跟在结构中另一个位域之后的位域将被打包到同一单元的相邻位中。如果剩余空间不足,则将不适合的位域放入下一个单元还是与相邻单元重叠是实现定义的。单元内位域的分配顺序(高位到低位或低位到高位)是实现定义的。未指定可寻址存储单元的对齐方式。
许多编译器都采用了“可寻址存储单元”是源中指定的类型的约定。例如,gcc
编译器不允许 9 位类型的位域unsigned char
,但它确实允许它用于unsigned int
类型。在您的示例中gcc
,Pentium 的编译器使用的结构unsigned char
大小为 1 个字节,而使用的结构大小unsigned int
为 4 个字节。许多编译器还采用了这样的约定,即如果一个位域不适合,它将不会与下一个单元重叠。然而,这可以使用标准的6.7.2.1 ad 12规定的 0 宽度位字段来强制执行
没有声明符但只有冒号和宽度的位域声明表示未命名的位域。作为一种特殊情况,宽度为 0 的位域结构成员表示不再将位域打包到放置前一个位域(如果有的话)的单元中。
如果您将位域与非位域混合,则6.7.2.1 ad 15规定位域和非位域的可寻址单元将具有不同的地址
在结构对象中,非位域成员和位域所在的单元的地址按声明顺序递增。一个指向结构对象的指针,经过适当的转换,指向它的初始成员(或者如果该成员是位域,则指向它所在的单元),反之亦然。结构对象中可能有未命名的填充,但不是在其开头。
一些架构的架构应用二进制接口 (ABI) 强制实施定义的选择,以确保该架构的不同编译器的互操作性。
哪个更好用,unsigned char 或 unsigned int,为什么?
unsigned int
以下内容不可移植,应将其考虑放在一边。
// Only portable bit field types are _Bool, signed int, unsigned int
// This is not portable.
struct data{
unsigned char a:3;
unsigned char b:4;
};
这留下了 OP 替代方案
struct data{
unsigned int a:3;
unsigned int b:4;
};
哪一个更适合用于内存分配,为什么?
如果最小内存是主要目标,请不要使用位字段。使用最小整数类型:unsigned char
在这种情况下。添加函数或定义以获取和设置。
void data_a_set(unsigned char *data, unsigned a) {
*data = *data & ~7u | a & 7u;
}
unsigned data_a_get(unsigned char data) {
return data & 7u;
}
// or for a more generic approach
#define B_BITS_PRIOR 3 /* sum of all previous bit widths */
#define B_BITS 4
#define MASK(w,p) (((1u << (w)) - 1) << (p))
void data_b_set(unsigned char *data, unsigned b) {
*data &= ~MASK(B_BITS, B_BITS_PRIOR);
*data |= MASK(B_BITS, B_BITS_PRIOR) & (b << B_BITS_PRIOR);
}
unsigned data_b_get(unsigned char data) {
return (data & MASK(B_BITS, B_BITS_PRIOR) >> B_BITS_PRIOR;
}
对于比 更宽的类型unsigned
,需要进行小的更改以确保更广泛的 and和shift操作。
unsigned char
struct
如果需要唯一类型,可以包装在 a中。
struct data {
unsigned char ab;
}