在 C 中的位域内格式化联合
Formatting Unions inside bitfields in C
我有一个特殊的问题,我有一个 16 位结构。在结构内部,在成员 "x_pos" 之后,根据一些外部标志,接下来的 5 位代表不同的东西。
如果设置了外部标志,则它们是旋转和缩放参数(rotscale_param),否则高两位是水平和垂直翻转位。
我尝试使用联合来表示此结构,但是当我使用 sizeof(attribute) 时,我希望看到 2 个字节,但结果是 4 个字节。
我的代码:
typedef unsigned short u16;
struct attribute {
u16 x_pos : 9;
union {
u16 rotscale_param : 5;
struct {
u16 unused : 3;
u16 hflip : 1;
u16 vflip : 1;
};
};
u16 size : 2;
};
如果有帮助,我正在尝试为此结构编写 C 代码:
OBJ Attribute
Bit Expl.
0-8 X-Coordinate
When Rotation/Scaling used:
9-13 Rotation/Scaling Parameter Selection
When Rotation/Scaling not used:
9-11 Not used
12 Horizontal Flip
13 Vertical Flip
14-15 OBJ Size
以上引用来源:http://problemkaputt.de/gbatek.htm#lcdobjoamattributes
这是一个可能的解决方案:
typedef struct attr_flag_set {
u16 x_pos : 9;
u16 rotscale_param : 5;
u16 size : 2;
};
typedef struct attr_flag_unset {
u16 x_pos : 9;
u16 unused : 3;
u16 hflip : 1;
u16 vflip : 1;
u16 size : 2;
};
union attribute_1 {
attr_flag_unset attr_unset;
attr_flag_set attr_set;
};
不过,我不确定这是否是理想的解决方案。
获得正确的包装并不容易。
此外,该标准仅在声明属于同一类型时才允许位打包。
从 ISO/IEC 9899 C99, 6.7.2.1 Structure and union specifiers
:
An implementation may allocate any addressable storage unit large
enough to hold a bitfield. If enough space remains, a bit-field that
immediately follows another bit-field in a structure shall be packed
into adjacent bits of the same unit. If insufficient space remains,
whether a bit-field that does not fit is put into the next unit or
overlaps adjacent units is implementation-defined. The order of
allocation of bit-fields within a unit (high-order to low-order or
low-order to high-order) is implementation-defined. The alignment of
the addressable storage unit is unspecified.
因此,当您在位域声明中插入位域结构时,这违反了规则并保留了另一个 WORD 用于存储。
唯一的解决方法是将声明恢复为 2 个不同结构的联合:
#pragma pack(1)
union attribute
{
struct
{
u16 x_pos : 9; //See the homogeneous series of declarations
u16 rotscale_param : 5;
u16 size : 2;
} struct1;
struct
{
u16 x_pos : 9; //See the homogeneous series of declarations here also
u16 unused : 3;
u16 hflip : 1;
u16 vflip : 1;
u16 size : 2;
} struct2;
};
这适用于 C99-C11。
不要忘记指示编译器打包数据。正如 Mah 让我注意到的那样,这可以在大多数编译器上使用 pragma 或在其他编译器上使用特定语法来完成。而且在这种情况下,打包可以是依赖于那些边界线应用程序的编译器。
P.S。还要考虑字节顺序依赖于编译器,但在很大程度上取决于 CPU 字节序,因此,在小端机器上运行良好的代码可能在大端机器上失败。
如果需要完全可移植的代码,最佳做法是使用掩码和移位。
如果您尝试使用 hflip
vflip
创建位文件,我认为您打算执行以下操作:
struct attribute {
u16 x_pos : 9;
union {
u16 rotscale_param : 5;
struct {
u16 unused : 3,
hflip : 1,
vflip : 1;
} hvflip;
};
u16 size : 2;
};
您实际使用 unused
、hflip
和 vflip
从单个 unsigned short
中创建一个 位域 .通过使用 3-complete unsigned shorts
来表示有问题的 5
位,按照另一个答案中所示的方式完全无视位域的 benefit/purpose 。您只需要 1 unsigned-short
来表示 16 位。
这是一个简单的例子:
#include <stdio.h>
typedef unsigned short u16;
struct attribute {
u16 x_pos : 9;
union {
u16 rotscale_param : 5;
struct {
u16 unused : 3,
hflip : 1,
vflip : 1;
} hvflip;
};
u16 size : 2;
};
void binprnpad (const unsigned long v, size_t sz);
int main (void) {
struct attribute attr = { .x_pos = 0b101010101,
.rotscale_param = 0b10101,
.size = 0b10 };
printf ("\n attr.x_pos (%3hu) : ", attr.x_pos);
binprnpad (attr.x_pos, 9);
printf ("\n attr.rotscale_param (%3hu) : ", attr.rotscale_param);
binprnpad (attr.rotscale_param, 5);
printf ("\n attr.hvflip.unused (%3hu) : ", attr.hvflip.unused);
binprnpad (attr.hvflip.unused, 3);
printf ("\n attr.hvflip.hflip (%3hu) : ", attr.hvflip.hflip);
binprnpad (attr.hvflip.hflip, 1);
printf ("\n attr.hvflip.vflip (%3hu) : ", attr.hvflip.vflip);
binprnpad (attr.hvflip.vflip, 1);
printf ("\n attr.size (%3hu) : ", attr.size);
binprnpad (attr.size, 2);
printf ("\n\n");
return 0;
}
void binprnpad (const unsigned long v, size_t sz)
{
if (!sz) { fprintf (stderr, "error: invalid sz.\n"); return; }
if (!v) { putchar ('0'); return; }
while (sz--)
putchar ((v >> sz & 1) ? '1' : '0');
}
输出
$ ./bin/struct_nested_bin
attr.x_pos (341) : 101010101
attr.rotscale_param ( 21) : 10101
attr.hvflip.unused ( 5) : 101
attr.hvflip.hflip ( 0) : 0
attr.hvflip.vflip ( 1) : 1
attr.size ( 2) : 10
如果您有任何问题,请告诉我。
我有一个特殊的问题,我有一个 16 位结构。在结构内部,在成员 "x_pos" 之后,根据一些外部标志,接下来的 5 位代表不同的东西。
如果设置了外部标志,则它们是旋转和缩放参数(rotscale_param),否则高两位是水平和垂直翻转位。
我尝试使用联合来表示此结构,但是当我使用 sizeof(attribute) 时,我希望看到 2 个字节,但结果是 4 个字节。
我的代码:
typedef unsigned short u16;
struct attribute {
u16 x_pos : 9;
union {
u16 rotscale_param : 5;
struct {
u16 unused : 3;
u16 hflip : 1;
u16 vflip : 1;
};
};
u16 size : 2;
};
如果有帮助,我正在尝试为此结构编写 C 代码:
OBJ Attribute
Bit Expl.
0-8 X-Coordinate
When Rotation/Scaling used:
9-13 Rotation/Scaling Parameter Selection
When Rotation/Scaling not used:
9-11 Not used
12 Horizontal Flip
13 Vertical Flip
14-15 OBJ Size
以上引用来源:http://problemkaputt.de/gbatek.htm#lcdobjoamattributes
这是一个可能的解决方案:
typedef struct attr_flag_set {
u16 x_pos : 9;
u16 rotscale_param : 5;
u16 size : 2;
};
typedef struct attr_flag_unset {
u16 x_pos : 9;
u16 unused : 3;
u16 hflip : 1;
u16 vflip : 1;
u16 size : 2;
};
union attribute_1 {
attr_flag_unset attr_unset;
attr_flag_set attr_set;
};
不过,我不确定这是否是理想的解决方案。
获得正确的包装并不容易。
此外,该标准仅在声明属于同一类型时才允许位打包。
从 ISO/IEC 9899 C99, 6.7.2.1 Structure and union specifiers
:
An implementation may allocate any addressable storage unit large enough to hold a bitfield. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.
因此,当您在位域声明中插入位域结构时,这违反了规则并保留了另一个 WORD 用于存储。
唯一的解决方法是将声明恢复为 2 个不同结构的联合:
#pragma pack(1)
union attribute
{
struct
{
u16 x_pos : 9; //See the homogeneous series of declarations
u16 rotscale_param : 5;
u16 size : 2;
} struct1;
struct
{
u16 x_pos : 9; //See the homogeneous series of declarations here also
u16 unused : 3;
u16 hflip : 1;
u16 vflip : 1;
u16 size : 2;
} struct2;
};
这适用于 C99-C11。
不要忘记指示编译器打包数据。正如 Mah 让我注意到的那样,这可以在大多数编译器上使用 pragma 或在其他编译器上使用特定语法来完成。而且在这种情况下,打包可以是依赖于那些边界线应用程序的编译器。
P.S。还要考虑字节顺序依赖于编译器,但在很大程度上取决于 CPU 字节序,因此,在小端机器上运行良好的代码可能在大端机器上失败。
如果需要完全可移植的代码,最佳做法是使用掩码和移位。
如果您尝试使用 hflip
vflip
创建位文件,我认为您打算执行以下操作:
struct attribute {
u16 x_pos : 9;
union {
u16 rotscale_param : 5;
struct {
u16 unused : 3,
hflip : 1,
vflip : 1;
} hvflip;
};
u16 size : 2;
};
您实际使用 unused
、hflip
和 vflip
从单个 unsigned short
中创建一个 位域 .通过使用 3-complete unsigned shorts
来表示有问题的 5
位,按照另一个答案中所示的方式完全无视位域的 benefit/purpose 。您只需要 1 unsigned-short
来表示 16 位。
这是一个简单的例子:
#include <stdio.h>
typedef unsigned short u16;
struct attribute {
u16 x_pos : 9;
union {
u16 rotscale_param : 5;
struct {
u16 unused : 3,
hflip : 1,
vflip : 1;
} hvflip;
};
u16 size : 2;
};
void binprnpad (const unsigned long v, size_t sz);
int main (void) {
struct attribute attr = { .x_pos = 0b101010101,
.rotscale_param = 0b10101,
.size = 0b10 };
printf ("\n attr.x_pos (%3hu) : ", attr.x_pos);
binprnpad (attr.x_pos, 9);
printf ("\n attr.rotscale_param (%3hu) : ", attr.rotscale_param);
binprnpad (attr.rotscale_param, 5);
printf ("\n attr.hvflip.unused (%3hu) : ", attr.hvflip.unused);
binprnpad (attr.hvflip.unused, 3);
printf ("\n attr.hvflip.hflip (%3hu) : ", attr.hvflip.hflip);
binprnpad (attr.hvflip.hflip, 1);
printf ("\n attr.hvflip.vflip (%3hu) : ", attr.hvflip.vflip);
binprnpad (attr.hvflip.vflip, 1);
printf ("\n attr.size (%3hu) : ", attr.size);
binprnpad (attr.size, 2);
printf ("\n\n");
return 0;
}
void binprnpad (const unsigned long v, size_t sz)
{
if (!sz) { fprintf (stderr, "error: invalid sz.\n"); return; }
if (!v) { putchar ('0'); return; }
while (sz--)
putchar ((v >> sz & 1) ? '1' : '0');
}
输出
$ ./bin/struct_nested_bin
attr.x_pos (341) : 101010101
attr.rotscale_param ( 21) : 10101
attr.hvflip.unused ( 5) : 101
attr.hvflip.hflip ( 0) : 0
attr.hvflip.vflip ( 1) : 1
attr.size ( 2) : 10
如果您有任何问题,请告诉我。