且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

为什么对象的地址会随着方法的不同而改变?

更新时间:2023-10-26 12:09:34

这就是 Rust 的工作原理.

That's just how Rust works.

不仅如此,这也是C 的工作方式:

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct {
    char* name;
    int32_t age;
} User;

void
UserInit(User* u){
    printf("in init: user addr: %p\n", u);
}

void
UserDoSomething(User* u){
    printf("in do something user addr: %p\n", u);
}

void
UserDestroy(User* u){
    free(u->name);
}

User rust_like_new(void) {
    User u;
    UserInit(&u);
    return u;
}

int main(int argc, char *argv[]) {
    User u = rust_like_new();
    UserDoSomething(&u);
}

in init: user addr:        0x7fff506c1600
in do something user addr: 0x7fff506c1630

通常,您不关心容器的地址,只关心它包含的内容.

Generally, you don't care about the address of the container, just what it contains.

如果我堆分配User,地址不会改变,但如果我使用Box (let u = Box::new(User::new())),它仍然会改变.

If I heap allocate User, the address won't change, but if I use Box (let u = Box::new(User::new())), it still changes.

同样的事情发生在 Rust 和 C 中.BoxUser * 本身的地址会改变.BoxUser *value(被指向的东西)将保持一致.

The same thing happens in Rust and C. The address of the Box<User> or User * itself will change. The value (the pointed-at thing) of the Box<User> or User * will stay consistent.

mod ffi {
    use std::mem;
    use std::os::raw::c_char;

    #[repr(C)]
    pub struct User {
        pub name: *const c_char,
        pub age: i32,
    }

    impl User {
        pub fn new() -> Box<User> {
            let mut ret: Box<User> = Box::new(unsafe { mem::uninitialized() });

            unsafe { UserInit(&mut *ret) }

            ret
        }

        pub fn do_something(&mut self) {
            unsafe { UserDoSomething(self) }
        }
    }

    extern "C" {
        pub fn UserInit(u: *mut User);
        pub fn UserDoSomething(u: *mut User);
    }
}

use ffi::User;

fn main() {
    let mut u = User::new();
    u.do_something();
}

in init: user addr:        0x10da17000
in do something user addr: 0x10da17000

如果您在将 User 的引用传递给 C 之前,它被移动到 Box 中,那么是的,移动时地址会改变进入 Box.这相当于:

If you pass a reference to User to C before it's moved into a Box, then yes, the address will change when it's moved into the Box. This would be the equivalent of:

User rust_like_new(void) {
    User u;
    UserInit(&u);
    return u;
}

int main(int argc, char *argv[]) {
    User u = rust_like_new();
    User *u2 = malloc(sizeof(User));
    *u2 = u;
    UserDoSomething(u2);
}

请注意,Rust(和其他语言)允许执行 RVO.但是,我相信打印地址会取消此优化的资格,因为如果启用 RVO,行为会发生变化.您需要查看调试器或生成的程序集.

Note that Rust (and other languages) allow for RVO to be performed. However, I believe that printing out the address would disqualify this optimization because the behavior would change if RVO was enabled. You'd need to look in a debugger or at the generated assembly.