将指针投入结构的第一个指向结构指针的指针是合法的生锈吗?

发布于 2025-02-05 00:31:10 字数 1194 浏览 1 评论 0原文

在C中,可以将指向结构的指针投放给指向其第一个成员的指针,反之亦然。也就是说,结构的地址被定义为其第一个成员的地址。

struct Base { int x; };
struct Derived { struct Base base; int y; };

int main() {
    struct Derived d = { {5}, 10 };
    struct Base *base = &d.base; // OK
    printf("%d\n", base->x);
    struct Derived *derived = (struct Derived *)base; // OK
    printf("%d\n", derived->y);
}

这通常用于实现C ++样式继承。

如果结构是repr(c)(以使其字段不重组),Rust是否允许使用同一件事?

#[derive(Debug)]
#[repr(C)]
struct Base {
    x: usize,
}

#[derive(Debug)]
#[repr(C)]
struct Derived {
    base: Base,
    y: usize,
}

// safety: `base` should be a reference to `Derived::base`, otherwise this is UB
unsafe fn get_derived_from_base(base: &Base) -> &Derived {
    let ptr = base as *const Base as *const Derived;
    &*ptr
}

fn main() {
    let d = Derived {
        base: Base {
            x: 5
        },
        y: 10,
    };

    let base = &d.base;
    println!("{:?}", base);

    let derived = unsafe { get_derived_from_base(base) }; // defined behaviour?
    println!("{:?}", derived);
}

该代码有效,但是它会始终有效吗?是否定义行为?

In C, a pointer to a struct can be cast to a pointer to its first member, and vice-versa. That is, the address of a struct is defined to be the address of its first member.

struct Base { int x; };
struct Derived { struct Base base; int y; };

int main() {
    struct Derived d = { {5}, 10 };
    struct Base *base = &d.base; // OK
    printf("%d\n", base->x);
    struct Derived *derived = (struct Derived *)base; // OK
    printf("%d\n", derived->y);
}

This is commonly used to implement C++-style inheritance.

Is the same thing allowed in Rust if the structs are repr(C) (so that their fields aren't reorganized)?

#[derive(Debug)]
#[repr(C)]
struct Base {
    x: usize,
}

#[derive(Debug)]
#[repr(C)]
struct Derived {
    base: Base,
    y: usize,
}

// safety: `base` should be a reference to `Derived::base`, otherwise this is UB
unsafe fn get_derived_from_base(base: &Base) -> &Derived {
    let ptr = base as *const Base as *const Derived;
    &*ptr
}

fn main() {
    let d = Derived {
        base: Base {
            x: 5
        },
        y: 10,
    };

    let base = &d.base;
    println!("{:?}", base);

    let derived = unsafe { get_derived_from_base(base) }; // defined behaviour?
    println!("{:?}", derived);
}

The code works, but will it always work, and is it defined behaviour?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情释 2025-02-12 00:31:11

您写的方式,目前不是;但是有可能使它起作用。

引用t仅允许访问t - 不再(它具有t的出处)。 expression & d.base为您提供了仅适用于基本 的参考。使用它访问派生的字段是未定义的行为。目前尚不清楚这是我们想要的,有积极的讨论(也 this ),但这就是当前行为。有一个名为 miri 的好工具 可以检查锈蚀代码是否存在某些(并非全部!)未定义的行为(您可以在操场上运行它;工具 - > miri),实际上它标记您的代码

error: Undefined Behavior: trying to reborrow <untagged> for SharedReadOnly permission at alloc1707[0x8], but that tag does not exist in the borrow stack for this location
  --> src/main.rs:17:5
   |
17 |     &*ptr
   |     ^^^^^
   |     |
   |     trying to reborrow <untagged> for SharedReadOnly permission at alloc1707[0x8], but that tag does not exist in the borrow stack for this location
   |     this error occurs as part of a reborrow at alloc1707[0x0..0x10]
   |
   = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
           
   = note: inside `get_derived_from_base` at src/main.rs:17:5
note: inside `main` at src/main.rs:31:28
  --> src/main.rs:31:28
   |
31 |     let derived = unsafe { get_derived_from_base(base) }; // defined behaviour?
   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^

您可以通过创建对整个 derived 的参考来使其正常运行到基础。 The raw pointer will keep the provenance of the original reference, and thus 将有效

// safety: `base` should be a reference to `Derived`, otherwise this is UB
unsafe fn get_derived_from_base<'a>(base: *const Base) -> &'a Derived {
    let ptr = base as *const Derived;
    &*ptr
}

fn main() {
    let d = Derived {
        base: Base {
            x: 5
        },
        y: 10,
    };

    let base = &d as *const Derived as *const Base;
    println!("{:?}", unsafe { &*base });

    let derived = unsafe { get_derived_from_base(base) };
    println!("{:?}", derived);
}

注:参考根本不应在此过程中 。如果您将base作为类型base的引用,则将再次失去出处。这将在操场上通过Miri,但根据当前规则仍然是未定义的行为,并且会以更严格的标志失败Miri(将环境变量设置miriflags > code>> zmiri-tag-raw-pointers < /代码>在本地运行Miri之前)。

The way you wrote it, currently not; but it is possible to make it work.

Reference to T is only allowed to access T - no more (it has provenance for T). The expression &d.base gives you a reference that is only valid for Base. Using it to access Derived's fields is undefined behavior. It is not clear this is what we want, and there is active discussion about that (also this), but that is the current behavior. There is a good tool named Miri that allows you to check your Rust code for the presence of some (not all!) undefined behavior (you can run it in the playground; Tools->Miri), and indeed it flags your code:

error: Undefined Behavior: trying to reborrow <untagged> for SharedReadOnly permission at alloc1707[0x8], but that tag does not exist in the borrow stack for this location
  --> src/main.rs:17:5
   |
17 |     &*ptr
   |     ^^^^^
   |     |
   |     trying to reborrow <untagged> for SharedReadOnly permission at alloc1707[0x8], but that tag does not exist in the borrow stack for this location
   |     this error occurs as part of a reborrow at alloc1707[0x0..0x10]
   |
   = help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
           
   = note: inside `get_derived_from_base` at src/main.rs:17:5
note: inside `main` at src/main.rs:31:28
  --> src/main.rs:31:28
   |
31 |     let derived = unsafe { get_derived_from_base(base) }; // defined behaviour?
   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can make it work by creating a reference to the whole Derived and casting it to a raw pointer to Base. The raw pointer will keep the provenance of the original reference, and thus that will work:

// safety: `base` should be a reference to `Derived`, otherwise this is UB
unsafe fn get_derived_from_base<'a>(base: *const Base) -> &'a Derived {
    let ptr = base as *const Derived;
    &*ptr
}

fn main() {
    let d = Derived {
        base: Base {
            x: 5
        },
        y: 10,
    };

    let base = &d as *const Derived as *const Base;
    println!("{:?}", unsafe { &*base });

    let derived = unsafe { get_derived_from_base(base) };
    println!("{:?}", derived);
}

Note: References should not be involved at all in the process. If you'll reborrow base as a reference of type Base, you will lose the provenance again. This will pass Miri on the playground, but is still undefined behavior per the current rules and will fail Miri with stricter flags (set the environment variable MIRIFLAGS to -Zmiri-tag-raw-pointers before running Miri locally).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文