大厂面经 | 小米 Go 实习一面:Golang 切片原理分享

今天给大家分享小米 Go 实习一面中与切片(Slice)相关的原理性面试题。本次分享会围绕切片的定义、数据结构、与数组的关系、扩容机制展开,同时老周有专门制作的视频讲解,想要详细了解此篇内容的同学可以移步小破站:老周聊golang,感谢支持关注!

一、切片(Slice)的基本定义

切片是 Golang 特有的数据结构,用法与可变长数组相似,但和数组有本质区别

  • 切片的核心是 “引用” 而非 “存储”:它本质是对底层数组某一段的引用,而非独立存储数据的结构,因此也被称为 “动态数组的视图”。
  • 数据修改的关联性:由于切片引用底层数组,对数组的修改会影响切片,对切片的修改也会同步影响底层数组。
  • 日常使用场景:平时可将切片当作可变长数组使用,简单操作(如遍历、添加元素)基本无问题,但需注意其引用特性带来的潜在风险(如意外修改底层数组)。

二、切片的数据结构

在 Golang 的 SDK 中,runtime/slice.go 文件定义了切片的结构体,包含三个核心字段:

array

指针,指向底层数组的某一个元素地址(并非固定指向数组索引 0,可从数组任意位置开始引用)

len

(长度)

表示切片当前引用的元素个数,即切片可直接访问的元素数量

cap

(容量)

表示切片最多能从底层数组引用的元素个数,取决于切片在底层数组的起始位置到数组末尾的元素总数

三、切片与底层数组的关系

为了更清晰理解二者关系,我们通过 “数组定义 + 切片引用” 的示例展开:

1. 示例基础:定义一个底层数组

假设定义一个包含 10 个元素的数组 arr,元素值与索引一致(0~9),数组地址空间连续:

var arr [10]int = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

2. 切片对数组的引用规则

若从数组 arr 的索引 2 开始引用,且截取 6 个元素,此时切片的特性如下:

  • array 指针指向:指向数组 arr 索引 2 的元素地址(即值为 2 的元素)。
  • len(长度):为 6,切片可访问的元素是数组 arr[2]~arr[7](共 6 个元素:2,3,4,5,6,7)。
  • cap(容量):为 8,因为从数组索引 2 到数组末尾(索引 9)共 8 个元素(2~9),这是切片最多能引用的元素总数。
  • 修改关联性:若修改切片的任意元素(如 slice[0] = 20),底层数组 arr[2] 的值也会变为 20;反之,修改 arr[3] = 30,切片 slice[1] 的值也会同步变为 30。

四、切片操作的代码与图示解析

基于上述底层数组 arr,通过具体代码操作,进一步理解切片的 arraylencap 变化:

1. 操作 1:创建完整引用数组的切片

在 64 位操作系统中,int 类型占 8 个字节,创建一个完全引用数组 arr 的切片 list

var list []int = arr[:] // 从数组索引 0 引用到末尾

  • array 指针:指向 arr[0] 的地址。
  • len:10(引用数组全部 10 个元素)。
  • cap:10(从数组索引 0 到末尾共 10 个元素)。
  • 地址特性:切片引用的元素地址连续,每个元素地址相差 8 个字节(符合 int 类型的字节大小)。

2. 操作 2:截取切片(半闭半开区间)

从切片 list 中截取 “索引 2 到索引 8” 的片段(Golang 切片截取遵循 半闭半开区间,即包含起始索引,不包含结束索引):

list1 := list[2:8] // 引用元素为 list[2]~list[7]
  • array 指针:指向 arr[2] 的地址(底层仍引用原数组 arr)。
  • len:6(8-2=6,共 6 个元素)。
  • cap:8(从 arr[2] 到 arr[9] 共 8 个元素,切片无法 “回溯” 引用起始位置之前的元素)。

3. 操作 3:全量复制切片(冒号前后不填)

对 list1 进行全量复制(冒号前后不填,表示从切片起始索引引用到末尾):

list2 := list1[:] // 等价于 list1[0:len(list1)]
  • 核心特性:仅复制切片结构体,不复制底层数组list2 的 array 指针仍指向 arr[2]len=6cap=8,与 list1 共享同一底层数组。
  • 注意:若修改 list2[0]list1[0] 和 arr[2] 会同步修改。

4. 操作 4:超出切片 len 但不超出 cap 的截取

基于 list1 截取 “索引 2 到索引 8” 的片段(list1 原 len=6,但 cap=8,允许截取到 cap 范围内的索引):

list3 := list1[2:8] // list1 的 cap=8,索引 8 在 cap 范围内(对应 arr[2+8=10]?不,list1 的 cap 是 8,即从 arr[2] 开始最多到 arr[2+8-1=9],所以索引 8 对应 arr[2+8=10]?此处原文档表述有误,正确逻辑:list1 的 `array` 指向 arr[2],其索引 0 对应 arr[2],索引 7 对应 arr[9],因此 list1[2:8] 中“8”实际是 list1 的 cap 边界,截取后 len=8-2=6,cap=8-2=6)
  • 本质:仍共享原数组 arrarray 指针指向 arr[4](list1 [2] 对应 arr [2+2=4]),len=6cap=6

5. 操作 5:切片扩容(append 超出 cap

对 list3 执行 append 操作(list3 原 len=6cap=6,已达容量上限):

list4 := append(list3, 10) // 添加元素 10,超出原 cap
  • 扩容触发条件:当 append 后切片的 len 超过原 cap 时,Golang 会创建新的底层数组,并将原切片的元素复制到新数组中,切片的 array 指针指向新数组。
  • 扩容后 list4 的特性:array 指针:指向新数组的起始地址(与原数组 arr 无关)。len:7(原 len=6 + 新增 1 个元素)。cap:12(原 cap=6,扩容时翻倍为 12,具体扩容规则见下一节)。独立性:修改 list4 的元素不会影响 list1、list2、list3 及原数组 arr。

五、切片的扩容机制

Golang 切片的扩容逻辑定义在 runtime/slice.go 的扩容方法中,核心是根据原切片的 cap 和新增元素后的 len 动态计算新容量(newCap),同时考虑内存对齐。

1. 扩容核心参数

  • oldLen:原切片的长度。
  • oldCap:原切片的容量。
  • newLenappend 后切片的总长度(oldLen + 新增元素个数)。
  • newCap:计算得出的新切片容量。
  • capmem:根据切片元素类型计算的内存空间(需满足内存对齐)。

2. 扩容流程(分步解析)

  1. 初始化新容量:先将 newCap 初始化为 oldCap(以原容量为基础计算)。
  2. 判断是否直接按新长度扩容:若 newLen > 2 * oldCap(新增元素后总长度超过原容量的 2 倍),则 newCap = newLen(直接按新长度分配容量,避免多次扩容)。
  3. 判断原容量是否小于 256:若 oldCap < 256,则 newCap = 2 * oldCap(原容量较小时,扩容为原容量的 2 倍,提升效率)。
  4. 原容量不小于 256 时的扩容规则:若 oldCap >= 256,则按公式 newCap = newCap + (newCap + 3*256)/4 迭代计算,直到 newCap >= newLen(原容量较大时,扩容幅度降低,避免内存浪费)。
  5. 内存对齐计算:根据切片元素类型的字节大小,计算 capmem(newCap * 元素字节大小),并确保 capmem 符合 Golang 的内存对齐规则(内存分配时会将空间切分为固定大小的块,需匹配块大小)。最终 newCap 需满足 capmem 对应的内存块大小,确保内存分配高效。

六、配套面试题资料说明

除了切片原理,本次分享还配套了小米 Go 实习面试相关的完整题库,涵盖以下领域:

  • Go 语言基础(如切片、map 原理、语法特性)。
  • 代码分析(切片操作、并发代码纠错等)。
  • 并发编程(goroutine、channel、sync 包等)。
  • 中间件与数据库(Redis、MySQL、MongoDB)。
  • 底层与运维(Linux 命令、Go Runtime、容器技术)。
  • 架构与分布式(微服务、消息队列、缓存、分布式系统)。

以上就是本次关于 Golang 切片原理的全部分享,以上就是老周今天的分享了,如果想要详细了解此篇内容的同学可以移步小破站:老周聊golang,观看视频讲解,Golang问题找老周,感谢支持关注!

#it##程序员##计算机##数据人的面试交流地#
全部评论

相关推荐

01-24 14:28
已编辑
南京信息工程大学 Java
有一堆投了没任何回信的,就记录下至少参与笔试的公司。有些公司没法记录过程或是忘记了,技术栈是java后端,因为是苏州人大多投的苏州或附近公司。参与笔试后没后文或被拒:神州信息、中国电信、极智嘉、中通快递、中国建筑、收钱吧、CVTE、小米、同程旅行、阅文集团主动放弃后续流程:苏宁易购、上海银行、中通服、苏州银行、江苏联通有面试:中国银行苏州分行-信息科技岗9.27线下笔试主要是考公题+英语+科技岗加试内容11.4线下一面包含无领导小组讨论与结构化单面12.2发差额体检12.16offer多益网络苏州9.2笔试9.8一面1.自我介绍2.项目难点3.bitmap存储签到信息为什么不用数组?4.sorted&nbsp;set底层除了跳表还有什么?5.介绍下跳表6.那为什么用跳表不用红黑树呢?7.介绍下策略模式8.手撕代码:父子继承,多态控制9.研究方向介绍下10.介绍下快排怎么做的,是否稳定?11.介绍一下堆排序12.一般什么问题会用到归并排序?13.讲讲cas14.讲讲mysql索引的底层15.那为什么sorted&nbsp;set底层不用b+树呢?16.讲讲对mysql锁的了解17.反问其中穿插了很多项目里的问题,基本完全结合项目提问。9.22HR面1.自我介绍2.什么时候开始求职的,主要投了哪些公司,是否有offer了?3.对做游戏的倾向?4.求职主要看重哪些方面因素?5.薪资期望?6.自己期望每天工作多久,一周休息几天?(挺无语的)7.对公司的了解有多少?8.为什么当时读研,有继续深造打算吗?9.研究方向是什么?10.为什么当时网申没填微博信息,平时用什么软件比较多?11.父母工作?12.测评阶段有没有印象比较深刻的题目,聊聊13.介绍下简历上的经历10.13二面(无后续)1.自我介绍2.找工作主要看重哪些方面呢?3.对游戏行业的了解4.来做游戏服务端开发有哪些优势?5.实习期间做的最复杂的业务6.实习期间下班后干什么?7.介绍下自己的项目8.对redis的了解9.实际项目里有用到redis持久化相关内容吗?10.有自己实际部署过哨兵相关内容吗?11.对java虚拟机有了解吗?12.讲讲线程池原理以及一些设计13.对数据库有什么了解?14.索引相关15.对多版本并发控制有什么了解?16.手撕代码:父子继承,多态控制博众精工10.11一面ai面(无后续)1.自我介绍2.分享在项目中遇到的棘手问题,是如何解决的?3.追问24.举例说明为了完成任务,去学习新技术的经历5.追问46.学习或工作中有没有遇到进展不顺利或想要放弃的时候,如何解决的?7.追问68.英语听力+口语题同盾科技9.21笔试10.14一面1.自我介绍2.项目问题3.分布式数据同步、数据一致性4.项目中jdk版本用的多少?5.jdk1.8中并发包相关6.线程池使用相关7.mysql索引8.B树和B+树区别9.截至目前做的自己最满意的一件事是什么10.手撕算法:简单题,链表相关10.24二面(无后续)1.自我介绍2.项目问题3.项目中有没有涉及到多线程和分布式的内容,介绍下4.gc机制了解吗?5.内存占用比较高,怎么解决?6.研究方向博云科技10.31一面1.自我介绍2.实习中技术难点3.具体项目问题4.未来发展方向?5.什么时候能来实习6.讲讲常用算法,快排归并7.讲讲对锁的了解8.讲讲最熟的map的底层、扩容等内容9.线程池相关10.java和python的区别,java的优点11.讲讲自己的优点、缺点12.反问11.2二面(无后续)1.自我介绍2.为什么没留在实习公司转正3.对大模型智能体了解4.java面向对象六大原则5.设计模式相关6.java集合相关7.mysql索引失效情况8.慢sql问题9.mysql锁相关10.什么时候行级锁会变成表级锁11.mysql主从复制12.有了解过gid吗?13.从库怎么切换成主库14.死锁什么情况下产生15.了解过生产者消费者模式吗?16.线程池相关17.未来发展方向18.反问无锡奥特维11.10一面(无后续)1.自我介绍2.讲讲spring&nbsp;boot,mysql,redis各个技术栈作用3.项目问题4.实习过程中架构和工作流程5.实习过程中遇到最大问题6.是不是自学的java,技术路线是什么,学习中间件的理由7.有没有研究过中间件源码8.讲讲xxl-job9.sql分页10.有没有用过其他数据库11.接触过哪些非关系数据库12.讲讲redis的数据结构及使用场景13.讲讲对aop的理解,底层原理14.讲讲工厂模式和策略模式15.讲讲线程池用了哪些设计模式16.讲讲sql里事务的相关语句17.对spring的ioc怎么理解的?18.如何实现依赖注入19.有没有了解过垃圾收集器20.相关垃圾收集算法21.有没有遇到服务起不来了或者oom问题,怎么处理的?22.讲讲用到的java集合23.反问民生银行苏州分行11.13一面线上无领导小组辩论,题目是大学开放相关,正反观点辩论11.24二面线下半结构群面12.8offer中金所技术苏州10.28笔试11.6一面&nbsp;hr+技术1.自我介绍2.项目问题3.研究方向4.对MCP的理解5.平时通过哪些平台学习?6.个人优缺点7.接口抽象类如何选择?8.讲讲反射9.说说代理的实现方式10.代理在spring的经典应用11.说说aop的几个核心元素12.线程的几种实现方式13.说说线程池实现与参数14.讲讲设计模式15.说说spring的一些注解16.讲讲实现事务的方式17.分布式如何实现并发安全18.有没有项目部署经验19.linux上查看进程命令20.linux查看磁盘大小命令21.数据库分页和分组函数22.反问11.21二面线下简历面12.3三面线下无领导小组讨论群面1.7offer海舟智能12.9一面线下面,先做一张小试卷,然后技术面12.12offer南京银行苏州分行10.30笔试11.27一面(无后续)线下单面,偏向半结构
点赞 评论 收藏
分享
评论
1
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务