为什么要验证结构体内存分布
内存分布是一个比较有意思的话题。
C语言的结构体,内存分布是按照成员顺序进行排列的。虽然结构体中实际的成员分布会受字节对齐方式的影响,但是成员的顺序是不会更改的。
构造一个结构体Demo,查看内存分布情况。
#include <stdio.h>
#pragma pack(4)
struct Demo {
short a;
int b;
char c;
};
#pragma pack()
int main() {
struct Demo demo = { 0 };
printf("size: %lu\n", sizeof(demo));
printf("Mem Addr:\n");
printf("a: %p, b: %p, c: %p\n", &demo.a, &demo.b, &demo.c);
}
如果按照4字节对齐,则结构体占用的空间为12字节,内存分布如下(.表示填充)
如果按照2字节对齐,则占用空间为8字节,内存分布如下
如果按照1字节对齐,则占用空间为7字节,内存分布省略。
RUST结构体的内存分布
上面的例子,我们使用RUST语言写一遍,代码如下
use std::mem::{size_of, aligh_of};
struct Demo {
pub a: i16,
pub b: i32,
pub c: i8
}
fn main() {
let demo = Demo { a: 0, b: 0, c: 0 };
println!("size: {}", size_of::<Demo>());
println!("align: {}", aligh_of::<Demo>());
println!("Mem Addr:");
println!("a: 0x{:x}, a: 0x{:x}, a: 0x{:x}",
&demo.a as *const i16 as usize,
&demo.b as *const i32 as usize,
&demo.c as *const i8 as usize);
}
不指定任何编译选项直接编译并运行。
结构体采用4字节对齐,但是结构体占用空间为8字节,和C语言写的结构体有差异,查看内存分布如下
可以看到,成员b的内存分布,居然在a的前面,和我们定义结构体的顺序有差异。
由此可以看到,rust编译器会对结构体成员的分布进行优化,尽可能节省空间。
RUST这样做事得益于其设计理念默认是不允许代码直接操作到裸指针,因此可以尽情优化内存排布。
当然RUST也支持内存分布按照C语言的规则来进行,需要对其使用repr过程宏修饰,其标准类型最好也要修改下,rust提供了libc这个crate,可以使用C语言的标准类型。
use std::mem::{size_of, aligh_of};
use libc::{c_short, c_int, c_char};
#[repr(C)]
struct Demo {
pub a: c_short,
pub b: c_int,
pub c: c_char
}
fn main() {
let demo = Demo { a: 0, b: 0, c: 0 };
println!("size: {}", size_of::<Demo>());
println!("align: {}", aligh_of::<Demo>());
println!("Mem Addr:");
println!("a: 0x{:x}, a: 0x{:x}, a: 0x{:x}",
&demo.a as *const c_short as usize,
&demo.b as *const c_int as usize,
&demo.c as *const c_char as usize);
此时再运行一下,查看结果。
结构体采用4字节对齐,结构体占用空间是12字节,内存排布如下
此时和C语言定义的结构体的内存分布就一样了。
我们也可以指定字节对齐方式为2字节对齐
#[repr(C, aligh(2))]
struct Demo {
pub a: c_short,
pub b: c_int,
pub c: c_char
}
此时得到的结果和C语言定义的2字节对齐的结构体是一样的。