`
zcmit
  • 浏览: 17821 次
文章分类
社区版块
存档分类
最新评论

Rust中文翻译13

 
阅读更多
目前为止,这机制工作的不错.但是不是所有的事情都是这样的.有时,你需要在不同的函数中传递内存数据,或者你需要把内存数据的生命期拉长到一个函数范围之外.为此,我们可以用堆.

在Rust语言中,你可以试用Box<T>类型来分配堆上的内存空间.这有个例子:
fn main() {
let x = Box::new(5);
let y = 42;
}

当main()函数调用的时候,内存是这样的:
地址 名字
1 y 42
0 x ???

我们在栈上分配了2个变量.y是42,正如以往的一样,那么x又是什么呢?x是一个Box<i32>,boxes是在堆上分配的内存.x的真正值是一个数据结构,它包含了一个指针,指向了堆.当我们开始执行这个函数的时候,Box::new()会被调用,这会在堆上分配一些空间,然后把5这个数放进去.内存现在看起来像这样:
地址 名字
230 5
... ... ...
1 y 42
0 x 230

Page 68

在1GB的内存中,我们假设堆的起始地址是230.因为栈地址是从0开始增加的,最简单的方法就是从另外一端开始分配.所以我们的第一个值就在内存的最高地址.另外,在x处的结构体是一个裸指针,它指向了我们分配的堆的地址,所以x的值就是230,我们分配这个地址的时候得到的值.

我们还没有真正讨论过分配和释放内存到底意味着什么.深入探讨这些内容超出了本书的范围,但是目前我们需要知道的重要的地方就是堆和栈具有相反的增长方向.本书的后面我们会有例子,因为我们可以任意的以任意的顺序分配和释放内存,这样就会在内存中形成一些"洞".这里有一个图显示出当程序运行了一段时间后的状态:
地址 名字
230 5
(230)-1
(230)-2
(230)-3 42
... ... ...
3 y (230)-3
2 y 42
1 y 42
0 x 230

在此例中,我们在堆上分配了4个变量,但是只释放了2个.在230和(230)-3之间有一段空隙,没有被使用.具体的细节取决于你管理内存的策略.不同的程序会使用不同的内存管理策略,通过一些库函数.Rust程序使用jemalloc来完成内存管理.

回到我们的程序来.因为这段空间分配在栈上,它可以比分配box的函数生存的更久.在此例,它并没有(生存更久).当函数结束的时候,我们会释放main()的栈帧.而Box<T>类型还有一个"小把戏":Drop.Box类型的Drop操作可以实现释放内存的功能.很好!所以当x离开作用域的时候,他释放了堆上的内存:
地址 名字
1 y 42
0 x ???

Page 69

然后栈帧也释放,内存也释放了.

4.1.4 参数和借用(borroing)
我们已经有了一些基本的关于栈和堆的例子,但是参数和借用呢?这有一个Rustxiaochengxu :
fn foo(i:&i32) {
let z = 42;
}

fn main() {
let x = 5;
let y = &x;

foo(y);
}

当我们进入main()函数是,内存是这样的:
地址 名字
1 y 0
0 x 5
x的值是基本数值5,y是x的一个引用.所以y的值就是x的内存地址,也就是0.

那么当我们调用foo()函数,并传入y参数呢?
地址 名字
3 z 42
2 i 0
1 y 0
0 x 5
栈帧并不仅仅针对局部变量,参数也是的.所以在此例中,我们同时需要参数i和z变量绑定.i就是参数y的一个拷贝.因为y的值是0,所以i也是0.

Page 70

借用并不会释放内存,原因是这样的:一个引用的值仅仅是一个指向内存地址的指针.如果我们不使用相应的内存,就无法正常工作.

4.1.5 一个复杂的例子
OK.让我们一步一的分析一个复杂的例子:
fn foo(x: &i32) {
let y = 10;
let z = &y;

baz(z);
bar(x, z);
}

fn bar(a: &i32, b: &i32) {
let c = 5;
let d = Box::new(5);
let e = &d;

baz(e);
}

fn baz(f: &i32) {
let h = 3;
let i = Box::new(20);
let j = &h;

foo(j);
}

首先,我们调用main()函数:
地址 名字
230 20
... ... ...
2 j 0
1 i 230
0 h 3

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics