为什么对象的地址会随着方法的不同而改变? [英] Why does the address of an object change across methods?

查看:39
本文介绍了为什么对象的地址会随着方法的不同而改变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有 C 代码要桥接.我选择先使用 mem::uninitialized 来声明内存,然后调用 C 函数(UserInit) 进行初始化,然后使用它(在 UserDoSomething).

I have C code to bridge to. I chose to use mem::uninitialized to claim the memory first, then call the C function(UserInit) to do initialisation, then use it (in UserDoSomething).

奇怪的是UserInitUserDoSomething 中对象的地址是不同的.为什么会这样?

The odd thing is the addresses of the object are different in UserInit and UserDoSomething. Why does it behave in this way?

C 代码:

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

Rust FFI:

use std::mem;
use std::os::raw::c_char;
use std::ffi::CString;


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

impl User {
    pub fn new()-> User {

        let ret: User = unsafe { mem::uninitialized() };

        unsafe {
            UserInit(&mut ret as *mut User)
        }

        ret
    }

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

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

锈蚀测试:

mod ffi;

use ffi::User;

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

理论上,它应该输出相同的地址,但它没有:

In theory, it should output the same address but it doesn't:

> cargo run
     Running `target/debug/learn`
in init: user addr: 0x7fff5b948b80
in do something user addr: 0x7fff5b948ba0

推荐答案

这就是 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.

这篇关于为什么对象的地址会随着方法的不同而改变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆