C++的指针和引用
指针
指针是一个变量,存储内存地址。可以指向任何地址,也可以为空。
int a = 10;
int* p = &a; // p存储a的地址
内存布局(32位系统):
地址 变量名 内存内容(十六进制) 说明
0x1000 a 0A 00 00 00 存储值10(小端序)
0x2000 p 00 10 00 00 存储a的地址0x1000
引用
引用是别名,创建时必须绑定到对象,之后不能重新绑定。
int a = 10;
int& r = a; // r是a的别名
内存布局(实现层面):
地址 变量名 内存内容(十六进制) 说明
0x1000 a 0A 00 00 00 存储值10(小端序)
0x2000 r 00 10 00 00 编译器一般实现为指针,指向0x1000
注:引用在语法层面是别名,实现层面通常是指针
Rust的借用
Rust的借用是通过引用( &T 和 &mut T )来实现的,借用是概念,引用是语法形式。分两种:
不可变借用
let a = 10;
let b = &a; // b借用a,不能修改a
可变借用
let mut a = 10;
let b = &mut a; // b可变借用a,可以修改a
关键区别
内存结构相同
指针、引用、借用,在内存中都是地址值。
Rust不可变借用
let a = 10;
let b = &a;
地址 变量名 内存内容(十六进制) 访问类型
0x1000 a 0A 00 00 00 原始值
0x2000 b 00 10 00 00 只读借用
Rust可变借用
let mut a = 10;
let b = &mut a;
地址 变量名 内存内容(十六进制) 访问类型
0x1000 a 0A 00 00 00 可修改
0x2000 b 00 10 00 00 可写借用
规则不同
C++引用:
- 创建后不能重新绑定
- 可以悬空(指向已销毁对象)
- 无借用限制
Rust借用:
- 有严格借用规则
- 编译期检查生命周期
- 防止数据竞争
借用规则
- 一个可变借用,或多个不可变借用
- 借用不能比原值活得长
- 编译期检查所有借用
示例对比
C++可能出问题:
int& get_ref() {
int x = 10;
return x; // 悬空引用
}
Rust编译期阻止:
fn get_ref() -> &i32 {
let x = 10;
&x // 编译错误:借用不能返回
}