Rust中u128与u32组合的结构体为何占32字节?如何调整?
Rust中#[repr(C)]结构体大小与对齐问题解答
为什么结构体大小是32字节而非20字节?
在#[repr(C)]布局下,结构体遵循C语言的内存布局规则,核心约束是对齐要求:
u128类型的大小为16字节,其对齐要求也为16字节(即变量地址必须是16的倍数)。u32类型大小4字节,对齐要求4字节。- 结构体的整体对齐要求由最大字段的对齐值决定,这里是16字节。
具体布局过程:
f1占据前16字节,满足自身16字节对齐要求。f2紧跟其后,占据16-20字节,满足自身4字节对齐要求。- 为了让整个结构体的大小是16字节的倍数(符合整体对齐要求),编译器会在
f2后面填充12字节的空白内存,最终结构体总大小为16+4+12=32字节。
如何调整为20字节且保证MMIO字段位置正确?
你的场景是MMIO,需要严格匹配硬件寄存器的偏移,不能有隐式填充。以下是两种可行方案:
方案1:显式字节数组封装(推荐,降低未对齐风险)
通过将结构体定义为固定大小的字节数组,手动实现字段的安全访问(需提前确认目标平台的未对齐访问规则):
#[repr(C)] struct Thing([u8; 20]); impl Thing { // 获取f1的不可变引用 pub fn f1(&self) -> &u128 { unsafe { &*(self.0.as_ptr() as *const u128) } } // 获取f2的不可变引用 pub fn f2(&self) -> &u32 { unsafe { &*(self.0.as_ptr().add(16) as *const u32) } } // 获取f1的可变引用(MMIO场景需谨慎使用可变操作) pub fn f1_mut(&mut self) -> &mut u128 { unsafe { &mut *(self.0.as_mut_ptr() as *mut u128) } } // 获取f2的可变引用 pub fn f2_mut(&mut self) -> &mut u32 { unsafe { &mut *(self.0.as_mut_ptr().add(16) as *mut u32) } } }
这种方式强制结构体大小为20字节,完全匹配MMIO的寄存器布局,同时通过方法封装避免直接操作原始指针的风险。若目标平台不允许未对齐的u128访问,此方法会触发未定义行为,需提前验证硬件特性。
方案2:带指定对齐的packed布局(需权衡平台兼容性)
官方不推荐直接使用#[repr(packed)]是因为它会将结构体对齐设为1字节,极易导致未对齐访问;但可以通过packed(n)指定结构体的对齐值,同时消除字段间的填充:
#[repr(C, packed(4))] struct Thing { f1: u128, f2: u32, }
此结构体的大小为16+4=20字节,对齐要求为4字节。注意事项:
f1的地址可能不满足u128的16字节对齐要求,在ARM、RISC-V等对未对齐访问严格的平台上,直接访问f1可能触发异常。- 若你的MMIO硬件寄存器本身按4字节对齐布局,且平台允许未对齐的
u128访问,此方案可直接使用。
内容的提问来源于stack exchange,提问作者Anthony McCann
相关产品推荐
相关产品推荐

