Rust中的特殊类型所占的内存大小

avatar
作者
猴君
阅读量:0

可以使用std::mem:size_of获取类型大小:

use std::mem::size_of;  struct Journal(String, u32); trait Summary {} impl Summary for Journal {}  fn main() {     println!("普通结构体相关:");     println!("{}", size_of::<&Journal>());     println!("{}", size_of::<Box<Journal>>());      println!("特征对象相关:");     println!("{}", size_of::<Box<dyn Summary>>());     println!("{}", size_of::<&dyn Summary>());     println!("{}", size_of::<&mut dyn Summary>());     println!("{}", size_of::<*const dyn Summary>());     println!("{}", size_of::<*mut dyn Summary>());      println!("切片相关:");     println!("{}", size_of::<&str>());     println!("{}", size_of::<&[i32]>());     println!("{}", size_of::<&[i32; 100]>());      println!("集合相关:");     println!("{}", size_of::<[i32; 100]>());     println!("{}", size_of::<Vec<i32>>());     println!("{}", size_of::<String>()); }  

输出

普通结构体相关: 8 8 特征对象相关: 16 16 16 16 16 切片相关: 16 16 8 集合相关: 400 24 24  

原因分析

Rust中,一个普通指针的大小为usize,与计算机CPU字长相等,对64位计算机来说usize=64/8=8字节

  • 对普通结构体来说,其引用/Box智能指针所占大小为usize,指示在内存中的起始位置即可,因为普通结构体编译时大小确定,运行时存放在栈上
  • 特征对象非常特殊,其引用/Box智能指针/裸指针占的大小均为2*usize
    • 这是因为特征对象编译时大小不确定,因此需要在运行时动态获知对象实例以及特征方法在内存中的位置,因此需要两个指针,分别指向它们
  • 对切片类型:
    • &str所占空间为2*usize,所引用字符串可能来自于堆内存/全局数据区,&str指示了所引用数据的起始位置和长度
    • &[i32]&str类似(&str是对&[u8]的封装),所占空间为2*usize,指示了所引用字符串在堆内存/栈内存中的起始位置和长度
    • &[i32; N]仅占一个usize,因为[i32; N]编译时大小已知,存放在栈上,&[i32; N]不需要像&[i32]那样额外记录长度
  • 对集合相关:
    • [i32; 100]编译时大小已知,存放在栈上,内存大小为4*100=400个字节
    • Vec动态数组的数据存放在堆上,Vec类型占三个usize的长度,分别指示数组在堆中的起始位置,数组长度以及数组当前的最大容量
    • String是对Vec<u8>的封装,其数据存放在堆上,String类型占三个usize的长度,分别指示字符串在堆中的起始位置,字符串长度以及字符串当前的最大长度

其实,VecString的本质都是智能指针,它们的大小都是固定的(大小不固定也没法通过编译啊),运行时存放在栈上。但是它们所包裹数据的长度是可变的,存放在堆上。它们的本质是通过存放在栈上的指针访问堆内存,来操作堆上包裹的数据。

综上所述,智能指针/引用所占内存大小是固定的,但是其指向的数据大小未必固定。对于所占内存固定已知的数据类型,运行时会把它们放在栈上,对于所占内存不固定的数据类型,运行时会把它们放在堆上,然后可以通过存放在栈上的指针来操作堆上的数据。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!