简体   繁体   中英

Rust: Struct has a `packed(N)`-attribute (N > 1) but there is still padding between the fields

TL;DR: I thought that the packed attribute in Rust always strips any padding between the fields but apparently this is only true for packed(1) .

I want my struct to represent the exact bytes in memory without any additional padding between fields but the struct also needs to be page-aligned. The compiler output isn't what I expect it to be in my code example. From the language reference [ 0 ] I found, that packed(N) aligns the struct to a N-byte boundary. I expected that only the beginning of the struct is aligned while there is never padding between the fields. However, I found out that:

#[repr(C, packed(4096)]
struct Foo {
  first: u8,
  second: u32,
}

let foo = Foo { first: 0, second: 0 };
println!("foo is page-aligned: {}", &foo as *const _ as usize & 0xfff == 0);
println!("{:?}", &foo.first as *const _);
println!("{:?}", &foo.second as *const _);
println!("padding between fields: {}", &foo.second as *const _ as usize - &foo.first as *const _ as usize);

results in

foo is page-aligned: false
0x7ffc85be5eb8
0x7ffc85be5ebc
padding between fields: 4

Why is the struct not page-aligned and why is there padding between the fields? I found out that I can achieve what I want with

#[repr(align(4096))]
struct PageAligned<T>(T);

#[repr(C, packed)]
struct Foo {
    first: u8,
    second: u32,
}


let foo = Foo { first: 0, second: 0 };
let aligned_foo = PageAligned(Foo { first: 0, second: 0 });

it results in

foo is page-aligned: true
0x7ffd18c12000
0x7ffd18c12001
padding between fields: 1

but I think this is counter-intuitive. Is this how it is supposed to work? I'm on Rust stable 1.57.

To meet the requirements with the available tools, your best option may be to construct a substitute for your u32 field which naturally has an alignment of 1:

#[repr(C, align(4096))]
struct Foo {
  first: u8,
  second: [u8; 4],
}

impl Foo {
    fn second(&self) -> u32 {
        u32::from_ne_bytes(self.second)
    }
    
    fn set_second(&mut self, value: u32) {
        self.second = u32::to_ne_bytes(value);
    }
}

This struct's layout passes your tests.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM