Go 面试题:SliceHeader 隐藏陷阱

大家好,我是煎鱼。

最近也是面试季+毕业季了,很多同学正在积极准备面试。尤其是很多同学,已经通过官网资料熟悉了 Go 基本语法,但没有太大把握。希望对一些常见的棘手面试问题做一些预习。

今天结合与此分享给大家,一起学习和进步!

面试问题

请先在脑子里思考一下具体的运行结果,再查看答案。

如下代码:

go 复制代码
func main() {
    s := make([]int, 0, 2)
    doSomething(s)
    fmt.Println(s)
}

func doSomething(a []int) {
    a = append(a, 1)
}
// 煎鱼干点什么...

面试问题:fmt.Println 的输出结果是什么?

问题解析

运行程序,查看输出结果:

css 复制代码
[]

fmt.Println 最终打印的是一个长度为 0 的切片。

答案是:空切片。(你答对了吗?)

在 Go 中,函数参数是按值传递的,这意味着上述代码在参数传递时,创建了参数值的副本并传递给函数。

而切片实际上是一个包含长度(len)、容量(cap)和指向底层数组指针(data)的结构体。

当我们将切片作为函数参数传递时,实质上复制的是切片的 SliceHeader,对应的底层数组是保持不变的。

结合代码来讲,就是因为在 doSomething 函数中,创建了 SliceHeader 的新副本。然后 append 函数会在超过容量时重新分配新切片,并返回更新后的切片。

深入验证

我们可以使用 unsafe 包去打印 SliceHeader(切片头),进行进一步的验证和分析。

如下代码:

go 复制代码
type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

func main() {
    s := make([]int, 0, 2)
    sh := (*SliceHeader)(unsafe.Pointer(&s))
    fmt.Println(sh)
    doSomething(s)
}

func doSomething(a []int) {
    a = append(a, 1)
    sh := (*SliceHeader)(unsafe.Pointer(&a))
    fmt.Println(sh)
}

输出结果:

arduino 复制代码
&{1374389592336 0 2} // main
&{1374389592336 1 2} // doSomething

两个切片的 Data 指针地址指向的是同一个底层数组。但由于长度不同,它们在应用的表现上是两个不同的切片。

这也印证了前面问题的结果是输出了空切片,切片长度为 0 的内部原理。

变通方法

这种情况下,建议是修改写法,提高代码易读性。否则后续维护也比较麻烦,不熟悉的同学咋一眼一看很有可能发现不了问题。

但如果你还是希望输出你想要的切片值,可以采取以下变通方法。

改动后的代码:

go 复制代码
func main() {
    s := make([]int, 0, 2)
    doSomething(s)
    fmt.Println(s[:1]) // 进行新的切片操作
}

func doSomething(a []int) {
    a = append(a, 1)
}

输出结果:

csharp 复制代码
[1]

原因是在进行 s[:1] 切片操作时,本质上是创建了一个新的 SliceHeader,所以可以正常打印和获取预期的元素。

当然,还有一种常见的写法就是切片 append 等变更后一定做一遍再赋值,这样可以规避掉不少使用上的细节坑。

总结

今天这篇文章讨论了一个很常见的 Go 面试问题,内容涉及切片作为函数参数的传递和修改。

重点在于切片作为参数是按值传递的,因此函数内部的修改不会影响外部变量。

如果仍然希望获取可以通过切片操作,重新切分一下新的切片结果集就可以了。

推荐阅读

相关推荐
用户7438356135110 小时前
无锁 Hub:我的 IM 系统为什么用 channel 而不是 mutex 管理在线用户
go
吴佳浩1 天前
Go史上最大“打脸”现场来了:泛型方法终于实现了
后端·go
明月_清风2 天前
深入 Go 并发编程:从 Goroutine 到 Channel 的系统性避坑指南
后端·go
用户34232323763172 天前
开源!Go+Wails+Vue3 手搓一个 PLC 实时监控桌面工具
go
止语Lab2 天前
为什么你的 Go TCP server P99 延迟这么高
go
Andy Dennis3 天前
nsq学习记录
消息队列·go·nsq
韦胖漫谈IT3 天前
选语言不是站队,是选适合问题的工具
java·python·ai·rust·go·技术落地
喵个咪3 天前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
夜悊3 天前
Go网络编程的学习代码示例:客户端/服务端(C/S)模型
go