GBA模拟器开发:如何用Union合并位域单比特且保持结构体为2字节
问题解答
你的需求完全可行,问题出在内部嵌套的union被编译器当作独立成员分配了额外空间,导致结构体总大小超出2字节。以下是两种可行的实现方案,既能保留每个字段的明确名称,又能严格保证结构体大小为2字节:
方案一:外层union共享位域
直接通过外层union为共享位的两个字段分别定义位域布局,确保它们复用同一个比特位:
#include <cstdint> struct OAMAttribute0 { union { uint16_t raw; // 完整属性布局 struct { uint16_t ypos: 8; // 0-255 uint16_t rotation_scaling: 1; // 0=off, 1=on uint16_t _shared_bit: 1; // 占位,对应共享位 uint16_t mode: 2; // 0=normal, 1=semi-transparent, 2=obj window, 3=prohibited uint16_t mosaic: 1; // 0=off, 1=on uint16_t colors: 1; // 0=16/16, 1=256/1 uint16_t shape: 2; // 0=square, 1=horizontal, 2=vertical, 3=prohibited } __attribute__((packed)); // 旋转缩放模式下的字段 struct { uint16_t : 9; // 跳过前9位(ypos+rotation_scaling) uint16_t double_size: 1; // 复用第9位 uint16_t : 6; // 跳过剩余位 } __attribute__((packed)); // 普通模式下的字段 struct { uint16_t : 9; // 跳过前9位 uint16_t obj_disable: 1; // 复用第9位 uint16_t : 6; // 跳过剩余位 } __attribute__((packed)); } __attribute__((packed)); }; static_assert(sizeof(OAMAttribute0) == 2, "OAMAttribute0 must be 2 bytes");
方案二:修正嵌套结构的packed属性位置
如果偏好原始的嵌套结构,只需把__attribute__((packed))移到包含位域的内层结构体上,而非嵌套的union本身:
#include <cstdint> struct OAMAttribute0 { union { uint16_t raw; struct { uint16_t ypos: 8; uint16_t rotation_scaling: 1; union { uint16_t double_size: 1; uint16_t obj_disable: 1; }; uint16_t mode: 2; uint16_t mosaic: 1; uint16_t colors: 1; uint16_t shape: 2; } __attribute__((packed)); // 内层结构体加packed,确保位域无padding }; } __attribute__((packed)); // 外层结构体也加packed,确保整体无额外空间 static_assert(sizeof(OAMAttribute0) == 2, "OAMAttribute0 must be 2 bytes");
关键说明
- 位域总位数必须严格等于16(8+1+1+2+1+1+2=16),这样编译器才会把整个结构体压缩到2字节。
__attribute__((packed))的作用是禁止编译器插入对齐padding,必须作用在包含位域的结构体上,而非内部的union。- 不同编译器(如GCC、Clang)对位域的处理可能有细微差异,
static_assert是验证结构体大小的关键手段,确保跨编译环境的一致性。
内容的提问来源于stack exchange,提问作者stubbie
相关产品推荐
相关产品推荐

