Sah*_*hae 10 unsafe undefined-behavior rust
&mut T
并&mut T
导致编译错误;太好了,两次可变地借款是客观上的错误。
是*mut T
和*mut T
未定义的行为,还是这是完全正确的事情?也就是说,可变指针别名有效吗?
是什么使事情变得更糟的是,&mut T
和*mut T
实际上编译和作品如预期,我可以通过引用,指针,然后再参考修改值...但我看到有人说,这是不确定的行为。是的,“有人说过”是我唯一的信息。
这是我测试的内容:
fn main() {
let mut value: u8 = 42;
let r: &mut u8 = &mut value;
let p: *mut u8 = r as *mut _;
*r += 1;
unsafe { *p += 1; }
*r -= 1;
unsafe { *p -= 1; }
println!("{}", value);
}
Run Code Online (Sandbox Code Playgroud)
当然,主要的问题是:
注意 —感谢trentcl 指出此示例在创建时实际上会导致复制p2
。可以通过用u8
非Copy
类型替换来确认。然后,编译器抱怨此举。可悲的是,这并不能使我更接近答案,仅是提醒我,仅由于Rust的move语义,我就可以得到意想不到的行为而不会成为未定义的行为。
fn main() {
let mut value: u8 = 42;
let p1: *mut u8 = &mut value as *mut _;
// this part was edited, left in so it's easy to spot
// it's not important how I got this value, what's important is that it points to same variable and allows mutating it
// I did it this way, hoping that trying to access real value then grab new pointer again, would break something, if it was UB to do this
//let p2: *mut u8 = &mut unsafe { *p1 } as *mut _;
let p2: *mut u8 = p1;
unsafe {
*p1 += 1;
*p2 += 1;
*p1 -= 1;
*p2 -= 1;
}
println!("{}", value);
}
Run Code Online (Sandbox Code Playgroud)
两种产量:
42
Run Code Online (Sandbox Code Playgroud)
这是否意味着两个指向相同位置并在不同时间被取消引用的可变指针不是未定义的行为?
我不认为在一开始就在编译器上进行测试不是一个好主意,因为未定义的行为可能会发生任何事情,甚至即使42
没有错误也可以进行打印。无论如何我都提到它,因为这是我尝试过的事情之一,希望能得到客观的答案。
我不知道如何编写一个测试来强制执行不稳定的行为,这将使它变得显而易见,因为它没有按预期使用,即使可能的话,也无法正常工作。
我知道这很可能是未定义的行为,无论如何在多线程环境中都会中断。但是,我希望得到比这更详细的答案,尤其是如果可变指针别名不是未定义的行为。(这实际上会很棒,因为尽管出于其他原因我使用Rust时,至少可以说是内存安全性……我希望仍然保留一支a弹枪,我可以将它指向任何地方,而不会被锁在脚上。我可以使用别名“可变指针”,而无需在C中投入精力。)
这是关于我是否可以的问题,而不是我是否应该这样做的问题。我想直接进入不安全的Rust中,只是要学习它,但是感觉像没有什么像C这样的“恐怖”语言一样,没有足够的信息来了解什么是未定义的行为,什么不是。
作者的注释:以下是直观的解释,而不是严格的解释。我不相信Rust中现在对“别名”有严格的定义,但是您可能会发现阅读Rustonomicon关于引用和别名的章节会有所帮助。
- 在任何给定时间,您都可以具有一个可变引用或任意数量的不可变引用。
- 引用必须始终有效。
没有“原始指针规则”。原始指针(*const T
和*mut T
)可以在任何地方别名任何东西,也可以根本不指向任何东西。
当取消引用原始指针,将其隐式或显式转换为引用时,可能会发生未定义的行为。即使在源代码中不是显式的,此引用仍必须遵守引用规则&
。
在第一个示例中,
unsafe { *p += 1; }
Run Code Online (Sandbox Code Playgroud)
*p += 1;
需要&mut
提及*p
的为了使用+=
运营商,因为如果你写
unsafe { AddAssign::add_assign(&mut *p, 1); }
Run Code Online (Sandbox Code Playgroud)
(编译器实际上并未AddAssign
用于实现+=
for u8
,但是语义是相同的。)
因为&mut *p
是另一个引用的别名,即r
违反了引用的第一个规则,从而导致未定义的行为。
您的第二个示例(由于编辑)有所不同,因为没有引用别名,只有另一个指针,并且没有管理指针的别名规则。因此,这
let p1: *mut u8 = &mut value;
let p2: *mut u8 = p1;
unsafe {
*p1 += 1;
*p2 += 1;
*p1 -= 1;
*p2 -= 1;
}
Run Code Online (Sandbox Code Playgroud)
在没有其他对的引用的情况下,value
声音是完美的。
归档时间: |
|
查看次数: |
158 次 |
最近记录: |