gpt4 book ai didi

rust - 别名可变原始指针 (*mut T) 会导致未定义的行为吗?

转载 作者:行者123 更新时间:2023-11-29 07:43:49 26 4
gpt4 key购买 nike

&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);
}

当然还有问题的要点:

注意 — 感谢 trentcl 的 pointing out this example actually causes a copy when creating p2 .这可以通过将 u8 替换为非 Copy 类型来确认。然后编译器提示移动。可悲的是,这并没有让我更接近答案,只是提醒我,我可以得到意想不到的行为而不是未定义的行为,这仅仅是因为 Rust 的移动语义。

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);
}

两者都产生:

42

这是否意味着指向同一位置并在不同时间被取消引用的两个可变指针不是未定义的行为?

我不认为在编译器上测试它是一个好主意,因为未定义的行为可能会发生任何事情,甚至打印 42 就好像没有错一样。无论如何我都会提到它,因为这是我尝试过的事情之一,希望得到一个客观的答案。

我不知道如何编写一个测试,该测试可能会强制出现不稳定的行为,这会使它变得非常明显,因为它没有按预期使用,因为它没有按预期使用,如果这样做是可能的话。

我知道这很可能是未定义的行为,无论如何都会在多线程环境中中断。不过,我希望得到比这更详细的答案,特别是如果可变指针别名不是未定义的行为。 (这实际上很棒,因为虽然我出于与其他人一样的原因使用 Rust - 内存安全,至少可以说......我希望仍然保留一把我可以指向任何地方的霰弹枪,而不会被锁定在我的脚上。我可以为“可变指针”设置别名,而不会在 C 语言中大吃一惊。)

这是一个关于我是否可以的问题,而不是关于我是否应该的问题。我想直面不安全的 Rust,只是为了了解它,但感觉没有足够的信息,这与 C 等“可怕”的语言不同,关于什么是未定义的行为,什么不是。

最佳答案

Author's note: The following is an intuitive explanation, not a rigorous one. I don't believe there is a rigorous definition of "aliasing" in Rust right now, but you may find it helpful to read the Rustonomicon chapters on references and aliasing.

rules of references (&T&mut T) 很简单:

  • At any given time, you can have either one mutable reference or any number of immutable references.
  • References must always be valid.

没有“原始指针规则”。原始指针(*const T*mut T)可以在任何地方为任何东西起别名,或者它们可以根本不指向任何东西。

当您取消引用 原始指针时,可能会发生未定义的行为,隐式或显式将其转换为引用。此引用仍然必须遵守引用规则,即使 & 在源代码中不明确也是如此。

在你的第一个例子中,

unsafe { *p += 1; }

*p += 1; 使用 &mut 引用 *p 以使用 += 运算符,就好像你写了

unsafe { AddAssign::add_assign(&mut *p, 1); }

(编译器实际上并没有使用AddAssignu8实现+=,但是语义是一样的。)

因为 &mut *p 被另一个引用别名,即 r,违反了引用的第一条规则,导致未定义的行为。

您的第二个示例(自编辑后)不同,因为没有对别名的引用,只有另一个指针,并且没有管理指针的别名规则。因此,这

let p1: *mut u8 = &mut value;
let p2: *mut u8 = p1;

unsafe {
*p1 += 1;
*p2 += 1;
*p1 -= 1;
*p2 -= 1;
}

在没有对 value 的任何其他引用的情况下,是完美的。

关于rust - 别名可变原始指针 (*mut T) 会导致未定义的行为吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57364654/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com