Rust中指针

起因

今天学习一下Rust中的指针.
Rust中指针类型,可简单分为3种

原生指针

//原生指针 *const (不可变) 和 *mut(可变)
let mut x = 32;
let  y = &mut x; //引用
unsafe {
    *y =64;  //通过原生指针 修改值
}

引用

//引用  通过&和&mut
let a = 100;
let b = &a;
println!("a={}", b);

智能指针

智能指针最早在c++得到了应用.智能指针是RAII(资源获取即初始化)和作用域范围的完美结合,在离开所在作用域的范围的进行资源的释放.
#ifndef ALGORITHM_MYCLASS_H
#define ALGORITHM_MYCLASS_H

#include <iostream>
using namespace  std;

class MyClass
{
public:
    char* p;
    MyClass()
    {
        cout << "调用构造函数" << endl;
        p = new char[1024 * 1024 * 100]();  //在堆上分配100m的空间
    }

    ~MyClass()              
    {
        cout << "调用析构函数" << endl;
        delete[] p;                       //在析构函数时,释放100m空间
    }
};


#endif //ALGORITHM_MYCLASS_H
int main(int argc, char *argv[]) {

    {
        vector<int> vec1{1, 2, 3};    //①在任务管理器中看当前程序所占内存大小
        unique_ptr<MyClass> myClass(new MyClass());  //unique_ptr 智能指针 在离开所在的作用域,自动释放资源
        vector<int> vec2{1, 2, 3};    //②在任务管理器中看当前程序所占内存大小
    }

    cin.get();                        //③任务管理器中看当前程序所占内存大小,
    //得出结论: 通过智能指针给myClass在堆上进行内存分配,在离开所在的作用域后,自动调用析构函数,并释放资源
    return 0;
}

未使用智能指针在堆上分配空间

使用智能指针在堆上分配100m空间

智能指针在离开所在作用域范围,自动调用析构函数,进行资源释放

智能指针分配内存和在离开作用域后,自动调用析构函数,进行资源释放

进入正题,Rust中最典型的智能指针非Box和String莫属,String使用最多,Rust中的智能指针都会实现Drop(没实现Drop,不会自动释放资源,能叫智能)和Deref(没有*,能叫指针),在Rust中类型默认是分配栈上的,可以通过Box(有点像C#的装箱操作,在IL中使用box进行装箱操作)分配到堆上.具体看下面的示例代码:

fn main() {
    {
        let p1 = Point { x: 1 };  //是在栈上进行的内存分配

        let point = Box::new(Point { x: 1 });  //通过Box的new函数在堆上开辟内存空间

        //Box实现Drop trait在离开所在作用域自动内存
    }
}

Box<T>源码,只有new/drop/deref.

/// A pointer type for heap allocation.
///
/// See the [module-level documentation](../../std/boxed/index.html) for more.
#[lang = "owned_box"]  //编译器实现内存分配和释放 由于实现drop trait可以自动释放资源
#[fundamental]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Box<T: ?Sized>(Unique<T>);

impl<T> Box<T> {
    /// Allocates memory on the heap and then places `x` into it.
    #[stable(feature = "rust1", since = "1.0.0")]
    #[inline(always)]
    pub fn new(x: T) -> Box<T> {
        box x  //box 在编译时,由编译器将box替换为exchange_malloc在离开作用域的是调用drop
    }
}


#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T: ?Sized> Drop for Box<T> {
    fn drop(&mut self) {
        // FIXME: Do nothing, drop is currently performed by compiler.
        //由编译器生成 释放资源代码
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Deref for Box<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self
    }
}

通过Rust编译器rustc,生成汇编代码,是可以看到exchange_malloc函数的,具体看下边生成的代码:

#通过--emit 可以指定 asm(汇编) llvm-ir(llvm中间代码) obj(目标文件)
rustc --emit asm  .\test.rs
_ZN5alloc5alloc15exchange_malloc17h0cb71b2e0aa90f51E:
.seh_proc _ZN5alloc5alloc15exchange_malloc17h0cb71b2e0aa90f51E
	subq	$72, %rsp
	.seh_stackalloc 72
	.seh_endprologue
	callq	_ZN4core5alloc6layout6Layout25from_size_align_unchecked17hab6091c15e4a755eE
	movq	%rax, 48(%rsp)
	movq	%rdx, 40(%rsp)
	leaq	.L__unnamed_2(%rip), %rcx
	movq	48(%rsp), %rdx
	movq	40(%rsp), %r8
	callq	_ZN62_$LT$alloc..alloc..Global$u20$as$u20$core..alloc..AllocRef$GT$5alloc17h589e7297af27cd37E
	movq	%rdx, 64(%rsp)
	movq	%rax, 56(%rsp)
	movq	56(%rsp), %rax
	testq	%rax, %rax
	sete	%cl
	movzbl	%cl, %edx
	movl	%edx, %eax
	jne	.LBB42_5
	jmp	.LBB42_7
.LBB42_7:
	movq	48(%rsp), %rcx
	movq	40(%rsp), %rdx
	callq	_ZN5alloc5alloc18handle_alloc_error17h96b5be050b7b98c9E
	ud2
	ud2
.LBB42_5:
	movq	56(%rsp), %rcx
	movq	64(%rsp), %rdx
	callq	_ZN4core3ptr8non_null26NonNull$LT$u5b$T$u5d$GT$10as_mut_ptr17h41487f386a6f2536E
	movq	%rax, 32(%rsp)
	movq	32(%rsp), %rax
	addq	$72, %rsp
	retq
	.seh_handlerdata
	.text
	.seh_endproc

	.def	 _ZN5alloc5alloc5alloc17ha8c49ddbf193d337E;
	.scl	3;
	.type	32;
	.endef
	.p2align	4, 0x90
_ZN5alloc5alloc5alloc17ha8c49ddbf193d337E:


秋风 2020-03-28