Files
cs-note/hzh/GolangStar/Go面试题库/Slice面试题.md
T

4.9 KiB
Raw Blame History

tags, create time
tags create time
go
golang
interview
slice-questions
2026-06-07 14:30

Slice 面试题 🍕

概述

本文件涵盖 Go Slice 的 4 道高频面试题,涉及底层结构、扩容机制、截取共享和函数传参等核心考点。Slice 是 Go 中最常用的数据结构之一,也是面试中的"必考题"。

关联笔记

正文

Q1:slice 的底层结构是怎样的? 🟢简单

[!question] ❓ 思考一下 如果让你设计一个能动态增长的数组,你会在结构体中放哪些字段?

参考答案

slice 是对数组的封装,描述一个数组的片段。其底层是一个包含三个字段的结构体:

// runtime/slice.go
type slice struct {
    array unsafe.Pointer // 指向底层数组的指针
    len   int           // 长度(当前元素个数)
    cap   int           // 容量(底层数组从起始位置可容纳的元素数)
}

[!note] 📝 核心考点 len ≠ cap 是理解 slice 的关键。len 是你"能看到"的元素数量,cap 是底层数组的实际容量。当 len == cap 时,append 会触发扩容。


Q2:slice 是怎么扩容的? 🟡中等

[!question] ❓ 思考一下 如果一个 slice 的 cap 是 10,你 append 第 11 个元素时会发生什么?扩容后的 cap 是多少?

参考答案

Go 1.17 及以前

条件 扩容规则
期望容量 > 当前容量的 2 倍 使用期望容量
len < 1024 容量翻倍(×2)
len >= 1024 容量增加 25%(×1.25),直到 >= 期望容量

Go 1.18 及以后(新规则)

条件 扩容公式
oldcap < 256 newcap = oldcap × 2
oldcap >= 256 newcap = oldcap + (oldcap + 3×256) / 4

[!warning] ⚠️ 高频陷阱 Go 1.18 的扩容规则更保守,减少了过度分配内存的情况。面试中如果不确定版本,可以先说旧规则再补充新规则的变化。

[!info] 🔗 延伸阅读


Q3:切片截取后修改会影响原切片吗? 🟡中等

[!question] ❓ 思考一下 s2 := s1[2:6:7],然后对 s2 做 append,什么时候会影响 s1,什么时候不会?

参考答案

关键判断标准:新切片是否触发了扩容。

slice := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s1 := slice[2:5]       // len=3, cap=8, 底层指向 slice[2]
s2 := s1[2:6:7]        // len=4, cap=5, 底层指向 slice[4]

s2 = append(s2, 100)   // 容量够,直接追加 → 影响底层数组
s2 = append(s2, 200)   // 容量不够,触发扩容 → 创建新数组,不影响 s1

s1[2] = 20             // 只影响 s1 可见范围

fmt.Println(s1)          // [2 3 20]
fmt.Println(s2)          // [4 5 6 7 100 200]
fmt.Println(slice)       // [0 1 2 3 20 5 6 7 100 9]

解析思路:

  1. 第一次 append(100):s2 cap=5,追加后 len=5,未超 cap,直接写入底层数组 → s1 能看到变化
  2. 第二次 append(200):s2 cap 已满,触发扩容创建新底层数组 → s1 看不到变化
  3. 修改 s1[2]:通过原始底层数组修改,但 s2 已有独立数组 → s2 不受影响

[!tip] 💡 面试技巧 回答这道题时,先画出三张图展示每次操作后的底层状态变化。面试官最看重的不是背答案,而是你能否清晰地描述数据流向。


Q4:slice 作为函数参数会改变原 slice 吗? 🟡中等

[!question] ❓ 思考一下 在函数里 append(s, 100) 后返回新的 slice,外层的 s 会变吗?为什么?

参考答案

分两种情况:

操作 外层 slice 结构变吗? 底层数据变吗?
修改元素值 s[i] = x 否(传的是副本) 是(共享底层数组)
append 后赋值给 s 否 否(s 只是副本)
传递 *[]int 指针 是 是
// 方法1:返回新 slice(推荐)
func myAppend(s []int) []int {
    return append(s, 100)
}
s = myAppend(s) // 需要重新赋值

// 方法2:传递指针
func myAppendPtr(s *[]int) {
    *s = append(*s, 100)
}
myAppendPtr(&s) // 直接修改

[!note] 📝 核心考点 slice 本身是结构体(三个字段),传入函数时被复制了一份。函数内修改底层数组的数据会影响外部,但修改 slice 结构本身(如让它的 array 指向别处)不会影响外部。

关联笔记