Rust中无对应共享引用时解引用裸指针为何触发段错误
核心错误原因
触发段错误的根本原因和引用本身没直接关系,是你写let ptr = val as *const i32;时,根本没有创建指向val变量的指针:val是i32类型的整数,值为1,直接用as强转的语义是把这个整数值1直接当作内存地址,构造一个指向地址0x1的*const i32指针。
地址0x1属于操作系统预留的低地址保护区,用户态程序根本没有访问权限,解引用这个指针自然会触发段错误。
错误代码复现:
fn main() { let val = 1; let ptr = val as *const i32; // 指针值是0x1,不是val的地址 unsafe { println!("{:?}", *ptr) }; // 访问非法地址,段错误 }
运行输出:
[1] 69212 segmentation fault (core dumped) cargo r
&符号在这里的作用 加了&之后的&val,语义是取变量val所在的内存地址,这个表达式的类型是&i32(共享引用),它存储的数值是val在栈上分配到的真实合法内存地址。这时候再把&i32强转为*const i32裸指针,只是去掉了Rust对引用的借用检查、所有权规则约束,指针存储的地址值没有任何变化,指向的内存位置确实存着整数1,解引用自然能拿到正确结果。
正确代码:
fn main() { let val = 1; let ptr = &val as *const i32; // 指针值是val的真实栈地址 unsafe { println!("{:?}", *ptr) }; // 正常输出1 }
运行输出:
1
常见误区澄清
你提到的“Rust引用本质是附带额外语义约束的指针”这个结论是对的,但你混淆了两类完全不同的类型转换逻辑:
- 引用转裸指针:属于同语义(都是代表内存地址的指针类型)、不同安全约束的类型转换,转换过程不会修改指针存储的地址值,只是把编译器能做安全检查的引用,换成需要开发者自行在
unsafe块里保证访问合法性的裸指针。 - 整数转裸指针:属于跨语义的构造操作,就是直接把整数的数值当作内存地址生成指针,这个操作本身是合法的(比如嵌入式开发里访问固定地址的硬件寄存器就会这么写),但前提是你传入的整数确实对应一块你有权限访问的合法内存,你代码里传入1显然不满足这个前提。
另外也不是必须通过“先取引用再强转”的方式创建指向变量的裸指针,Rust标准库提供了addr_of!宏,可以直接取变量地址得到裸指针,不需要先创建共享引用,一般用在要指向未初始化内存、避免引用对齐要求这类特殊场景:
fn main() { let val = 1; let ptr = std::ptr::addr_of!(val); // 直接得到*const i32类型指针,不需要&val unsafe { println!("{:?}", *ptr) }; // 同样正常输出1 }
内容的提问来源于stack exchange,提问作者Ishaan Goel
相关产品推荐
相关产品推荐

