-
Stack VS Heap
1.1 Stack按值的接收顺序来存储,按相反的顺序将他们移除(后进先出,lifo)
添加数据叫做压入栈
移除数据叫做弹出栈1.2 所有存储在Stack上的数据必须拥有已知的固定的大小
编译时大小未知的数据或运行时大小可能发生变化的数据必须存放到heap上1.3 Heap内存组织差一些
当你把数据放入heap时,你会请求一定数量的空间
操作系统在heap里中找到一块足够大的空间,把他标记为在用,并返回一个指针,也就是这个空间的地址
这个过程叫做在heap上进行分配,有时仅仅称为“分配”1.4 把值压到stack上不叫分配
1.5 因为指针是已知固定大小的,可以把指针存放到stack上
但如果想要到实际数据,你必须使用指针来定位1.6 把数据压到stack上要比在head上分配快得多
因为操作系统不需要寻找用来存储新数据的空间,哪个位置永远都在stack的顶端1.7 在head上分配空间需要做更多的工作
操作系统首先需要找到一个足够大的空间来存储数据,然后要做好记录方便下次分配1.8 访问heap中的数据要比访问stack中的数据慢,因为需要通过指针才能找到heap中的数据
对于现代的处理器来说,由于缓存的缘故,如果指令在内存中跳转的次数越少,那么速度就越快1.9 如果数据存放的距离比较近,那么处理器的处理速度就会更快一些(stack上)
1.10 如果数据之间的距离比较远,那么处理速度就会慢一些(heap上)
在heap上分配大量的空间也是需要时间的1.11 当你的代码调用函数时,值被传入到函数(也包括指向head到指针)函数本地的变量被压到stack上,当函数结束后,这些值会从stack上弹出
1.12 所有权解决的问题
跟踪代码的那些部分正在使用heap的那些数据
最小化heap上的重复数据量
清理heap上未使用的数据以避免空间不足 -
变量作用域
scope就是程序中一个项目的有效范围
fn main() {
let s1 = 2;
let _s2 = s1;
println!("{}", s1);
}
// 运行结果 2
fn main() {
let mut s1 = String::from("mut");
let _s2 = s1;
println!("{}", s1);
}
// 运行结果 报错 value borrowed here after move
-
所有权和函数
最后看几个例子
fn main() {
let s2 = 2;
println!("我是整数没有发生copy前:{}", s2);
copy_methods(s2);
println!("我是整数copy后:{}", s2);
}
fn copy_methods(s2: u32){
println!("{}", s2)
}
// 运行结果
我是整数没有发生copy前:2
2
我是整数copy后:2
fn main() {
let s2 = 2;
println!("我是整数没有发生copy前:{}", s2);
let s4 = copy_methods(s2);
println!("我是整数copy后:{}", s2);
println!("我是返回值:{}", s4);
println!("我是返回值:{}", s4);
}
fn copy_methods(s2: u32) -> u32{
let s3 = s2 + 1;
s3
}
// 运行结果
我是整数没有发生copy前:2
我是整数copy后:2
我是返回值:3
我是返回值:3
fn main() {
let mut s1 = String::from("mut");
println!("我是没有发生移动前:{}", s1);
take_methods(s1);
println!("我是发生移动后:{}", s1);
}
fn take_methods(s1: String){
println!("{}", s1);
}
// 运行结果 报错 value borrowed here after move
由上面的例子我们可以看出,像这种整形,确定大小的数据,被当作参数传递过去时,发生了复制,而像字符串这种不确定大小的数据,则发生了移动(移出了作用域)
-
借用与引用
fn main() {
let s1 = String::from("mut");
println!("我是没有发生移动前:{}", s1);
// 发生了借用
let s2 = take_methods(&s1);
println!("我是发生移动后:{}-{}", s1, s2);
}
fn take_methods(s: &String) -> usize{
// 借用的东西是不能修改的,只能借用,而不拥有所有权
s.len()
}
// 执行结果:
// 我是没有发生移动前:mut
// 我是发生移动后:mut-3
fn main() {
let mut s1 = String::from("mut");
println!("我是没有发生移动前:{}", s1);
// 发生了 可变引用
let s2 = take_methods(&mut s1);
println!("我是发生移动后:{}-{}", s1, s2);
}
fn take_methods(s: &mut String) -> usize{
// 可变引用可以进行操作
s.push_str("new");
println!("我是添加后的字符串{}", s);
s.len()
}
// 执行结果:
// 我是没有发生移动前:mut
// 我是添加后的字符串mutnew
// 我是发生移动后:mutnew-6