Pointers 如何从成员指针获取包含结构的指针?

Pointers 如何从成员指针获取包含结构的指针?,pointers,rust,Pointers,Rust,我有一种类型: struct Foo { memberA: Bar, memberB: Baz, } 我知道有一个指针指向Foo中的memberB: p: *const Baz let p2 = p as usize - ((&(*(p as *const Foo)).memberB as *const _ as usize) - (p as usize)); 获取指向原始结构的新指针的正确方法是什么 我当前的实现如下所示,我很确定由于取消了(p as*co

我有一种类型:

struct Foo {
    memberA: Bar,
    memberB: Baz,
}
我知道有一个指针指向
Foo
中的
memberB

p: *const Baz
let p2 = p as usize -
    ((&(*(p as *const Foo)).memberB as *const _ as usize) - (p as usize));
获取指向原始结构的新指针的正确方法是什么

我当前的实现如下所示,我很确定由于取消了
(p as*const Foo)
的引用,
p
不是指向
Foo
的指针,因此调用了未定义的行为:

p: *const Baz
let p2 = p as usize -
    ((&(*(p as *const Foo)).memberB as *const _ as usize) - (p as usize));
这是FFI的一部分-我无法轻松地重新构造代码以避免需要执行此操作


这与Rust非常相似,但对于Rust,据我所知,它没有宏的偏移量。

解引用表达式产生一个左值,但该左值实际上不是从中读取的,我们只是对其进行指针数学运算,所以理论上,它应该定义得很好。不过这只是我的解释

我的解决方案包括使用一个空指针来检索字段的偏移量,因此它比您的要简单一些,因为它避免了一次减法(我们将减去0)。我相信我看到一些C编译器/标准库通过从空指针返回字段地址来实现
offsetof
,这就是下面解决方案的灵感所在

fn main() {
    let p: *const Baz = 0x1248 as *const _;
    let p2: *const Foo = unsafe { ((p as usize) - (&(*(0 as *const Foo)).memberB as *const _ as usize)) as *const _ };
    println!("{:p}", p2);
}
我们还可以定义自己的
偏移量宏:

macro_rules! offset_of {
    ($ty:ty, $field:ident) => {
        unsafe { &(*(0 as *const $ty)).$field as *const _ as usize }
    }
}

fn main() {
    let p: *const Baz = 0x1248 as *const _;
    let p2: *const Foo = ((p as usize) - offset_of!(Foo, memberB)) as *const _;
    println!("{:p}", p2);
}

通过的实现,现在可以在没有结构实例和调用未定义行为的情况下获取结构中字段的地址

不幸的是,从Rust 1.47开始,该能力并不稳定:

#![功能(原始参考宏)]
使用std:{mem::maybeuniit,ptr};
结构示例{
a:i32,
b:u8,
c:布尔,
}
fn main(){
让偏移量=不安全{
让base=maybeunit:::uninit();
让base_ptr=base.as_ptr();
设c=ptr::raw_const!(*base_ptr.c);
(c作为usize)-(base_ptr作为usize)
};
println!(“{}”,偏移量);
}
这一点的实现既棘手又微妙。最好使用维护良好的板条箱,例如。您可以使用
不稳定_raw
功能标志选择此不稳定功能


在稳定此功能之前,必须具有结构的有效实例。您可以使用诸如
once\u cell
之类的工具来最小化需要创建的虚拟值的开销:

use once_cell::sync::Lazy; // 1.4.1

struct Example {
    a: i32,
    b: u8,
    c: bool,
}

static DUMMY: Lazy<Example> = Lazy::new(|| Example {
    a: 0,
    b: 0,
    c: false,
});

static OFFSET_C: Lazy<usize> = Lazy::new(|| {
    let base: *const Example = &*DUMMY;
    let c: *const bool = &DUMMY.c;
    (c as usize) - (base as usize)
});

fn main() {
    println!("{}", *OFFSET_C);
}
use once_cell::sync::Lazy;//1.4.1
结构示例{
a:i32,
b:u8,
c:布尔,
}
静态虚拟:Lazy=Lazy::new(| |示例{
答:0,,
b:0,
c:错,
});
静态偏移量_C:Lazy=Lazy::new(| |){
let base:*常量示例=&*DUMMY;
设c:*const bool=&DUMMY.c;
(c为usize)-(基本为usize)
});
fn main(){
println!(“{}”,*偏移量_C);
}
若您必须在编译时拥有这个,那个么您可以将类似的代码放入构建脚本中,并写出一个带有偏移量的Rust源文件。但是,这将跨越多个编译器调用,因此您依赖的结构布局在这些调用之间不会改变。使用具有已知表示形式的内容可以降低这种风险

另见:


为什么不能将
*const Foo
传递给C代码呢?在这个特定的示例中,FFI给我一个
*const Baz
,我需要从中检索原始对象。如果我可以简单地传递原始对象,我会这样做,但这不是一个选项。注意:这目前不适用于静态和常量,并且在这些作用域中不稳定。注意:不幸的是,取消引用空指针(现在)是UB,请参见最后一节关于offsetof woes。我相信,如果这个RFC被接受,将是合理的。强调相信。