Go 语言陷阱 - 数组和切片
https://geektutu.com/post/hpg-gotchas-array-slice.html
源代码/数据集已上传到 Github - high-performance-go
1 第一个陷阱
1.1 下面程序的输出是
1
|
func foo(a [2]int) {
|
1.2 答案
正确的输出是 [1 2]
,数组 a
没有发生改变。
- 在 Go 语言中,数组是一种值类型,而且不同长度的数组属于不同的类型。例如
[2]int
和[20]int
属于不同的类型。 - 当值类型作为参数传递时,参数是该值的一个拷贝,因此更改拷贝的值并不会影响原值。
我们在 切片(slice)性能及陷阱 这篇文章中也提到了,为了避免数组的拷贝,提高性能,建议传递数组的指针作为参数,或者使用切片代替数组。
1.3 更多
如果将上述程序替换为:
1
|
func foo(a *[2]int) {
|
或
1
|
func foo(a []int) {
|
输出将会变成 [200 2]
。
在 切片(slice)性能及陷阱 这篇文章中,我们也提到了切片由三个值构成:
*ptr
指向底层数组的指针len
长度cap
容量
因此,将切片作为参数时,拷贝了一个新切片,即拷贝了构成切片的三个值,包括底层数组的指针。对切片中某个元素的修改,实际上是修改了底层数组中的值,因此原切片也发生了改变。
2 第二个陷阱
2.1 下面程序的输出是
1
|
func foo(a []int) {
|
2.2 答案
输出仍是 [1 2]
,切片 a
没有发生改变。
传参时拷贝了新的切片,因此当新切片的长度发生改变时,原切片并不会发生改变。而且在函数 foo
中,新切片 a
增加了 8 个元素,原切片对应的底层数组不够放置这 8 个元素,因此申请了新的空间来放置扩充后的底层数组。这个时候新切片和原切片指向的底层数组就不是同一个了。因此,对新切片第 0 个元素的修改,并不会影响原切片的第 0 个元素。
如果如果希望 foo
函数的操作能够影响原切片呢?
两种方式:
- 设置返回值,将新切片返回并赋值给
main
函数中的变量a
。 - 切片也使用指针方式传参。
1
|
func foo(a []int) []int {
|
或
1
|
func foo(a *[]int) {
|
上述两个程序的输出均为:
1
|
[200 2 1 2 3 4 5 6 7 8]
|
从可读性上来说,更推荐第一种方式
去重
func removeDuplicateElementForRule(addrs []string) []string { | |
result := make([]string, 0, len(addrs)) | |
temp := map[string]struct{}{} | |
for _, item := range addrs { | |
if _, ok := temp[item]; !ok { | |
temp[item] = struct{}{} | |
result = append(result, item) | |
} | |
} | |
return result | |
} | |
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)