2015-06-14 82 views
1

时,我有一个简单的例子锈的行为不符合我的精神形象,所以我想知道我缺少什么:堆栈行为返回一个指针到局部变量

fn make_local_int_ptr() -> *const i32 { 
    let a = 3; 
    &a 
} 

fn main() { 
    let my_ptr = make_local_int_ptr(); 
    println!("{}", unsafe { *my_ptr }); 
} 

结果:

3 

这不是我所期望的。使用The Stack and the Heap

给出的符号我希望堆栈帧看起来像这样:

Address | Name | Value 
----------------------- 
    0 | a | 3 

make_local_int_ptr(),但此行之后,

let my_ptr = make_local_int_ptr(); 

由于a超出范围,我期望堆栈被清除。但它显然没有。

而且,如果我定义创建my_ptr并打印了它的提领值之间的另一个变量:

fn main() { 
    let my_ptr = make_local_int_ptr(); 
    let b = 6; 
    println!("{}", b); // We have to use b otherwise Rust 
         // compiler ignores it (I think) 
    println!("{}", unsafe { *my_ptr }); 
} 

我的输出是:

6 
0 

这又不是我所期待的,我是思考:

Address | Name | Value 
----------------------- 
    0 | b | 6 

在这种情况下,我的输出将是:

6 
6 

,甚至(在C++Go我得到这样的结果):

Address | Name | Value 
----------------------- 
    1 | b | 6 
    0 | a | 3 

在这种情况下,我的输出是:

6 
3 

但为什么我收到的输出我正在接受?

此外,为什么返回一个指向本地变量的指针甚至允许?该变量超出范围,并且指针指向的值变得不可预知。

+0

当一个函数有许多局部变量时,通常当函数开始时它们会一次性全部分配。因此,在调用'make_local_int_ptr'时,已经分配了'b'(即使它还不可用),所以'a'和'b'不会重叠。 –

+5

[您租用酒店房间。你把书放在最上面的抽屉里。你第二天早上退房,但“忘记”放弃你的钥匙。一个星期后,你回到酒店,不要登记入住,用偷来的钥匙偷偷摸进你的旧房间,然后看着抽屉。你的书还在那里。惊人(http://stackoverflow.com/a/6445794/395760) – delnan

+0

@delnan:即使到了现在,我只是喜欢这样的解释:) –

回答

7

根本不应该返回一个指向本地堆栈变量的指针。这样做是未定义的行为,编译器完全可以自由地执行任何想要的操作。

当您说unsafe时,您承诺编译器会手动维护所有预期的不变量,然后立即违反该承诺。

说穿了:你违反记忆安全,所有投注都关闭。解决的办法是不是那样做。


要解释为什么你可能然而看到这种行为,(再次,这是不确定的行为,任何保证):在某种意义上说,它的覆盖与堆栈不是“清除”零;它再也无法读取它了。

而且,因为make_local_int_ptr通话结束时,编译器没有理由保存它的堆栈空间,所以它可以重复使用的空间任何东西。由于致电println!0可能是