面试题答案
一键面试内存布局差异
- 数组:
- 数组是固定大小的,其内存布局是在栈上连续分配的。例如,
let arr: [i32; 5] = [1, 2, 3, 4, 5];
,这5个i32
类型的元素在栈上是紧密排列的,大小为5 * std::mem::size_of::<i32>()
字节。数组的大小在编译时就确定,并且不能改变。
- 数组是固定大小的,其内存布局是在栈上连续分配的。例如,
- 切片:
- 切片是动态大小的视图,它本身是一个胖指针(fat pointer),在64位系统上,切片的大小为16字节(8字节指向数据的指针,8字节表示切片长度)。切片并不拥有数据,而是借用其他数据结构(如数组或堆上分配的Vec)的数据。例如,
let s: &[i32] = &arr[1..3];
,切片s
指向arr
数组中的部分数据,切片本身的内存布局是在栈上,而实际的数据仍在arr
所在的内存位置。
- 切片是动态大小的视图,它本身是一个胖指针(fat pointer),在64位系统上,切片的大小为16字节(8字节指向数据的指针,8字节表示切片长度)。切片并不拥有数据,而是借用其他数据结构(如数组或堆上分配的Vec)的数据。例如,
对使用场景的影响
- 数组:
- 固定大小场景:当需要一个固定大小的数据集合,并且希望在栈上分配内存以提高性能和简单性时,使用数组。比如一个游戏中固定数量的玩家位置数组,每个玩家的位置可以是一个
(i32, i32)
的元组,由于玩家数量在游戏设计时就确定,使用数组很合适。
let player_positions: [(i32, i32); 10] = [(0, 0); 10];
- 固定大小场景:当需要一个固定大小的数据集合,并且希望在栈上分配内存以提高性能和简单性时,使用数组。比如一个游戏中固定数量的玩家位置数组,每个玩家的位置可以是一个
- 切片:
- 动态大小或借用场景:当需要处理动态大小的数据块或者需要借用其他数据结构的数据时,使用切片。例如,在读取文件内容时,不知道文件具体大小,读取的数据可以用
&[u8]
切片来处理。
use std::fs::File; use std::io::{Read, Seek, SeekFrom}; let mut file = File::open("example.txt").expect("Failed to open file"); let file_size = file.seek(SeekFrom::End(0)).expect("Failed to seek"); file.seek(SeekFrom::Start(0)).expect("Failed to seek"); let mut buffer = vec![0; file_size as usize]; file.read(&mut buffer).expect("Failed to read"); let slice: &[u8] = &buffer;
- 切片在函数参数传递中也很常用,因为它允许函数处理不同大小的数据块,而不需要关心具体的数据所有者,提高了代码的通用性。例如:
fn process_slice(slice: &[i32]) { for num in slice { println!("{}", num); } } let arr: [i32; 5] = [1, 2, 3, 4, 5]; process_slice(&arr); let vec: Vec<i32> = vec![6, 7, 8]; process_slice(&vec);
- 动态大小或借用场景:当需要处理动态大小的数据块或者需要借用其他数据结构的数据时,使用切片。例如,在读取文件内容时,不知道文件具体大小,读取的数据可以用