裸指针的分类
- *mut T 可变裸指针
- *const T 不可变裸指针
如何获取裸指针?
1. 从引用获取
struct A;
let a = &A;
let o: *const A = a as *const A;
2. 堆上分配
struct A;
let a = A;
let a: *const A = Box::into_raw(Box::new(a));
3. 内存分配器
GlobalAlloc::alloc
裸指针的一些特性
1. 不释放所指向的对象
struct A;
impl Drop for A {
fn drop(&mut self) {
println!("drop A...");
}
}
fn main() {
let a = A;
let b = Box::into_raw(Box::new(a));
}
执行这段代码并不会打印drop A。换句话说,释放裸指针并不会释放它所指向的对象。裸指针b的释放不会同时释放其指向的内存结构。
补充:令Rust内存泄漏的几种方法:
- Box::leak(Box::new(a))
- mem::forget(a)
- ManuallyDrop::new(a)
这几个底层都是ManuallyDrop,任何被这个struct封装的结构都不会被释放。
2. 解引用裸指针是不安全的
fn main() {
let mut a: i32 = 99;
add_one(&mut a as *mut i32);
println!("{}",a);
}
fn add_one(pointer: *mut i32) {
unsafe { *pointer += 1 };
}
// 100
3. 解引用不能move出指针
struct A;
fn main() {
let a = A;
let x = &a as *const A;
// 这里报错
let mm :A = unsafe { *x };
}
read可以吗:
struct A;
fn main() {
let a = A;
let x = &a as *const A;
// let mm:A = unsafe { *x };
let mm:A = unsafe { x.read() };
}
看起来没报错。我们看read这个函数的注释:
/// Reads the value from
src
without moving it. This leaves the/// memory in
src
unchanged.
实际上 read 也不会move原数据。
4. ZST
Box上的ZST的裸指针,打印出来地址是0x1,跟注释里描述的一样,Box::new对于 ZST 实际上是没有发生内存申请的。
裸指针该如何释放呢?
指向栈的裸指针
裸指针指向栈的:指针,内存也会随着栈帧一起释放。
指向堆上的裸指针
Box::into_raw 将返回堆上的裸指针,对于此类指针,内部是 ManuallyDrop ,会阻止堆上内存的释放。我们拿着这样的裸指针,咋释放呢?
1. 从裸指针还原回box
fn main() {
let b = Box::into_raw(Box::new(A));
let o = unsafe { Box::from_raw(b) };
}
struct A;
impl Drop for A {
fn drop(&mut self) {
println!("drop A");
}
}
如此还原Box后,Box释放,内存即释放。
2. 直接释放裸指针
先执行下struct的Drop,就是调用drop_in_place,然后调用dealloc释放内存。
use std::{
alloc::{dealloc, Layout},
ptr::drop_in_place,
};
struct A;
impl Drop for A {
fn drop(&mut self) {
println!("drop AA")
}
}
fn main() {
let a = Box::into_raw(Box::new(A));
unsafe { drop_in_place(a) };
unsafe { dealloc(a as *mut u8, Layout::new::<A>()) };
}