为什么对象的地址会随着方法的不同而改变? [英] Why does the address of an object change across methods?
问题描述
我有 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
).
奇怪的是UserInit
和UserDoSomething
中对象的地址是不同的.为什么会这样?
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 useBox
(let u = Box::new(User::new())
), it still changes.
同样的事情发生在 Rust 和 C 中.Box
或 User *
本身的地址会改变.Box
或 User *
的 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屋!