MST

星途 面试题库

面试题:Go语言中Slice的内存布局特点

在Go语言中,Slice是一种复合数据类型,请描述Slice的内存布局是怎样的?它与数组在内存布局上有哪些主要区别?
10.6万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

Slice的内存布局

  1. 结构组成:在Go语言中,Slice本质上是一个包含三个字段的结构体。这三个字段分别是:
    • 指向底层数组的指针:该指针指向Slice所引用的底层数组的第一个元素。通过这个指针,Slice能够访问到底层数组的数据。
    • 长度(length):表示Slice当前包含的元素个数。长度是Slice可以访问的元素数量的上限,并且不能超过底层数组的容量。
    • 容量(capacity):表示从Slice的起始元素开始,到其底层数组末尾的元素个数。容量反映了Slice在不重新分配内存的情况下,最多能容纳多少个元素。
  2. 示例:假设有如下代码:
s := make([]int, 5, 10)

这里创建了一个Slice,其底层数组是一个可以容纳10个int类型元素的数组。Slice当前长度为5,容量为10。其内存布局中,指向底层数组的指针指向数组第一个元素的内存地址,长度字段值为5,容量字段值为10。

Slice与数组在内存布局上的主要区别

  1. 固定与动态
    • 数组:数组的长度在声明时就固定下来,其内存布局是一段连续的内存空间,大小为数组元素类型大小乘以数组长度。例如var a [5]int,数组a在内存中占据5 * sizeof(int)大小的连续空间,且这个空间大小在数组生命周期内不会改变。
    • Slice:Slice的长度是动态可变的。虽然它基于底层数组,但自身的长度可以在一定范围内变化(只要不超过容量)。其内存布局除了指向底层数组的指针,还有长度和容量字段,这使得Slice在使用上比数组更灵活。
  2. 内存管理方式
    • 数组:数组是值类型,当数组作为函数参数传递时,会完整地复制一份。这意味着函数内部对数组的修改不会影响原始数组,除非传递的是数组指针。例如:
func modifyArray(arr [5]int) {
    arr[0] = 100
}
var a [5]int
modifyArray(a)
// a[0] 仍然是初始值0,因为函数内修改的是副本
- **Slice**:Slice作为函数参数传递时,传递的是其结构体(包含指针、长度和容量),这意味着函数内部对Slice的修改可能会影响原始Slice,因为它们共享底层数组。例如:
func modifySlice(s []int) {
    s[0] = 100
}
s := make([]int, 5)
modifySlice(s)
// s[0] 变为100,因为函数内修改的是同一个底层数组
  1. 容量特性
    • 数组:数组没有容量的概念,其大小就是声明时指定的长度。
    • Slice:Slice有容量的概念,容量决定了在不重新分配内存的情况下,Slice能扩展到多大。当Slice的长度增长到等于容量时,如果再追加元素,就需要重新分配内存,通常会分配一个更大的底层数组,并将原数组的数据复制过去。