Rust与C对比:为何String字符的引用内存地址均相同?
Rust与C指针/引用机制对比问题
我正在对比Rust与C的指针/引用机制,编写了大致等价的代码,目标是完成以下操作:
- 声明一个字符串
- 创建指向字符串首个元素的指针
- 对每个元素执行三个操作:
- 打印原始指针(内存地址)
- 通过解引用指针打印字符本身
- 对步骤2得到的值取引用,再次打印内存地址
C中所有步骤的结果符合预期,但Rust里步骤3的结果不符合预期。想知道如何在Rust中实现这类unsafe指针操作(创建指针、解引用、对解引用结果取引用)。
Rust代码及输出
Rust 源码
fn main() { //Init String, pointer and get String length for iterator let my_str = String::from("York"); let my_str_ptr: *const u8 = my_str.as_ptr(); println!("Original string: {my_str}"); for i in 0..my_str.len() { unsafe { //Offset pointer by i let chr_ptr: *const u8 = my_str_ptr.offset(i as isize); let chr: char = *chr_ptr as char; println!("Deref raw pointer: {chr}"); println!("Raw pointer: {chr_ptr:p}"); println!("Ref to value: {:p}", &chr); } } }
Rust 输出
Original string: York Deref raw pointer: Y Raw pointer: 0x561e134b8ba0 Ref to value: 0x7ffd962f665c Deref raw pointer: o Raw pointer: 0x561e134b8ba1 Ref to value: 0x7ffd962f665c Deref raw pointer: r Raw pointer: 0x561e134b8ba2 Ref to value: 0x7ffd962f665c Deref raw pointer: k Raw pointer: 0x561e134b8ba3 Ref to value: 0x7ffd962f665c
C代码及输出
C 源码
#include <stdio.h> int main(void) { char *my_str = "York"; char *my_str_ptr = my_str; printf("Original string: %s\n", my_str); while (*my_str_ptr != '\0') { printf("Deref raw pointer: %p\n", my_str_ptr); printf("Raw pointer: %c\n", *my_str_ptr); printf("Ref to value: %p\n\n", &(*my_str_ptr)); my_str_ptr++; } return 0; }
C 输出
Original string: York Deref raw pointer: 0x559af0d44004 Raw pointer: Y Ref to value: 0x559af0d44004 Deref raw pointer: 0x559af0d44005 Raw pointer: o Ref to value: 0x559af0d44005 Deref raw pointer: 0x559af0d44006 Raw pointer: r Ref to value: 0x559af0d44006 Deref raw pointer: 0x559af0d44007 Raw pointer: k Ref to value: 0x559af0d44007
问题原因
不是Rust格式化的问题,而是代码逻辑的差异:
- Rust代码中,
let chr: char = *chr_ptr as char;是把原字符串内存中的u8值拷贝到了栈上的新变量chr,&chr取的是这个栈变量的地址,自然和原指针地址不一致。 - C中
&(*my_str_ptr)不会产生中间拷贝,编译器会直接优化为原指针,所以取到的是原字符串的内存地址。
解决方案
要获取原字符串中字符的地址,不能创建中间拷贝变量,而是直接对原指针解引用后取引用(需在unsafe块中操作,同时确保原字符串生命周期有效)。
修改后的Rust代码:
fn main() { let my_str = String::from("York"); let my_str_ptr: *const u8 = my_str.as_ptr(); println!("Original string: {my_str}"); for i in 0..my_str.len() { unsafe { let chr_ptr: *const u8 = my_str_ptr.offset(i as isize); // 直接创建指向原内存的引用,无拷贝 let chr_ref: &u8 = &*chr_ptr; let chr: char = *chr_ref as char; println!("Deref raw pointer: {chr}"); println!("Raw pointer: {chr_ptr:p}"); println!("Ref to value: {:p}", chr_ref); } } }
或者更简洁的写法,直接在打印时创建引用:
println!("Ref to value: {:p}", unsafe { &*chr_ptr });
注意事项
因为my_str是String,在整个循环期间都处于有效生命周期内,所以上述unsafe操作是安全的,不会出现悬垂引用问题。
内容的提问来源于stack exchange,提问作者hut_rudder
相关产品推荐
相关产品推荐

