Rust中的各种指针
xtutujs、golang、rust、关注他49 人赞同了该文章Rust 中的指针大体可以分为以下四种:引用 references“胖指针 fat pointers”(该分类存有争议)智能指针 smart pointers裸指针 raw pointers1. 引用就是直接对一个变量执行 &、&mut 操作,永远不会为 null。其占用的大小与 usize 一致。与引用相关的一个名词是 借用(borrowing)。其定义为:将获取引用作为函数参数称为 借用(borrowing)fn test_ref(){ let mut num = 5; let num_ref = &mut num; *num_ref = 100; println!("{} sizeof &i32 {}", num, std::mem::size_of::<&i32>()) // output 100 sizeof &i32:8}2. 胖指针比引用多了些其它数据。在 rust 中,属于胖指针的数据:切片( &[T] )。 T 为切片内元素的类型。切片类型存有: "地址(ptr) + 长度(len) "两个字段。图中红框内,就是一个切片。
fn test_fat_pointers(){ let mut arr = [1, 2, 3, 4]; let slice = &mut arr[1..4]; slice[0] = 100; println!("{:?}", arr); // [1, 100, 3, 4] let slice = &arr[1..4]; println!("{:p} {:?}", slice, unsafe { transmute::<_, (usize, usize)>(slice) }); // Output: 0x8a6c0ff4ac (594518471852, 3) // 0x8a6c0ff4ac 594518471852 这两个值是相等的。 // (594518471852, 3) 分别表示 具体数据的堆地址 和 长度 两个字段。 // 注意这里是用 slice,而不是 &slice。(&slice表示这个变量本身的栈地址) println!("sizeof &[i32]:{}", std::mem::size_of::<&[i32]>()); // Output: sizeof &[i32]:16 // 因为包含了两个字段:地址 + 长度,所以其占用内存为 2 个 usize 类型大小}3. 智能指针有特殊功能的,实现了 Deref、drop 相关的trait。在 rust 中,属于智能指针的数据:String、 Vec<T>、Box、Cell、Rc 等等。每一个智能指针的实现都不大一样,这里就不再展开叙述。仅以 Box 举例:fn test_smart_pointers(){ // 将本应存在栈上的地址,存在了堆上 let mut num = Box::new(1); // num_address 指向 box 里面的具体内容(也就是储存在堆上的数值 1) let num_address : *mut i32 = &mut *num; unsafe { *num_address = 100 } println!("{}", *num + 100) // Output: 200}4. 裸指针类似 C 语言里面的指针,可以为 null !创建裸指针,是 safe 的,读写裸指针,才需要 unsafe !裸指针又可以分为可变、不可变,分别写作 *mut T 和 *const T这里的星号不是解引用运算符,它是类型名称的一部分。这里的 T 表示指针指向的具体类型,裸指针本身的的类型大小与 usize 一致。评论区有朋友指出:裸指针也能指向不定长类型,而且跟指向不定长类型的引用具有一样的内存布局。所以调用 sizeof,获得的大小是原始类型的大小。(不过指针这个变量本身的占用大小应该还是 一个 usize)详见评论区的讨论fn test_raw_pointers(){ let mut num = 1; // 将引用转为裸指针 let num_raw_point = &mut num as *mut i32; unsafe { *num_raw_point = 100; println!("{} {} {:p}", num, *num_raw_point, &num); // Output: 100 100 0x8d8c6ff6bc } let address = num_raw_point as usize; // 将一个 usize 对象,转化为 裸指针 let raw = address as *mut i32; unsafe { *raw = 200; println!("{} {} {:p} {}", num, *num_raw_point, &num, address); // Output: 200 200 0x8d8c6ff6bc 607946536636 }}