面试题答案
一键面试栈内存和堆内存分配策略的主要区别
- 栈内存:
- 分配方式:栈内存分配非常快,遵循后进先出(LIFO)原则。当一个函数调用时,其局部变量会被分配到栈上,函数结束时,这些变量所占的栈空间会自动释放。
- 内存管理:由系统自动管理,不需要程序员手动干预内存的释放。
- 数据特点:适合存储大小在编译时已知且生命周期较短的数据。
- 堆内存:
- 分配方式:堆内存分配相对较慢,因为需要在堆空间中查找合适的内存块来满足分配请求。分配的内存块不一定是连续的。
- 内存管理:需要程序员手动管理内存的分配和释放(在Rust中通过所有权系统自动管理,避免了手动管理的一些常见错误,如内存泄漏和悬空指针)。
- 数据特点:适合存储大小在编译时未知或生命周期较长的数据。
变量分配到栈上的情况
当变量的类型是固定大小且在编译时已知,并且其生命周期与所在作用域相关联时,变量会分配到栈上。例如,基本数据类型(如整数、浮点数、布尔值等)和元组(如果其元素也是固定大小类型):
fn main() {
let num: i32 = 42; // i32类型固定大小,分配到栈上
let tuple = (1, "hello"); // 元组,i32固定大小,字符串字面量在编译时也有固定大小,整体分配到栈上
}
变量分配到堆上的情况
- 动态大小类型:例如
String
类型,其大小在编译时是未知的,因为字符串内容可以动态改变。
fn main() {
let s = String::from("hello"); // String类型的数据分配在堆上,栈上只存放指向堆内存的指针、长度和容量信息
}
- Box类型:
Box
用于将数据分配到堆上,常用于需要在堆上存储数据但又想在栈上保留一个指向堆数据的指针的情况。
fn main() {
let b = Box::new(42); // i32类型的数据被放在堆上,栈上存放指向堆中i32数据的指针
}