blog icon indicating copy to clipboard operation
blog copied to clipboard

Go 语言陷阱 - 数组和切片 | Go 语言高性能编程 | 极客兔兔

Open geektutu opened this issue 3 years ago • 22 comments

https://geektutu.com/post/hpg-gotchas-array-slice.html

Go 语言/golang 高性能编程(high performance go),Go 语言进阶教程,Go 语言陷阱(gotchas)。这篇文章介绍了 Go 语言中数组(Array) 和切片(Slice)的常见陷阱和规避方式。例如数组作为参数,修改参数,原数组不会发生改变。

geektutu avatar Dec 05 '20 06:12 geektutu

大佬真的牛批,起初知道对slice的修改会影响到原来的slice,就以为添加操作也有可能会被修改。无论slice的长度多少,只要对slice的添加操作都是会生成一个新的slice,原来的slice不受影响。感谢

Fxskyzz avatar Dec 16 '20 08:12 Fxskyzz

看完收工

bestgopher avatar Dec 16 '20 09:12 bestgopher

@chocolateszz 你的理解是对的,不同切片维护了 len 和 cap,添加操作,不改变 len 长度以前的底层数组的元素,所以不影响。

geektutu avatar Dec 16 '20 15:12 geektutu

@bestgopher 哈哈,后面会增加新的文章,逐步增加难度~

geektutu avatar Dec 16 '20 15:12 geektutu

厉害 期待大佬的新文章 受益匪浅~

zpng avatar Jan 05 '21 12:01 zpng

厉害 期待大佬的新文章 受益匪浅~

@zpng 感谢认可,近期会频繁更新哒~

geektutu avatar Jan 06 '21 02:01 geektutu

想到 slice 只要不扩容,就不会重新分配空间,其实可以提前分配好空间,避免在调用的函数内部扩容。但是由于整个 slice 是作为传值传入函数内部的,所以 slice 结构体内部的 len 在函数结束后,依然是原来的长度。
我尝试了手动修改 slice 结构体内部的 len ,算是第三种 foo 函数影响原切片的方法(滑稽 🤣

func foo(a []int) {
        a = append(a, 1, 2, 3, 4, 5, 6, 7, 8)
        a[0] = 200
}

func main() {
        // 提前分配空间,防止扩容修改空间
        a := make([]int, 0, 20)
        a = append(a, 1, 2)

        foo(a)
        fmt.Println(a)

        // slice struct from https://golang.org/src/runtime/slice.go
        pointerOfa := unsafe.Pointer(&a)

        pointerSize := unsafe.Sizeof(pointerOfa)

        intSize := unsafe.Sizeof(int(0))

        internalLengthPointer := (*int64)(unsafe.Pointer(uintptr(pointerOfa) + uintptr(pointerSize)))
        internalCapPointer := (*int64)(unsafe.Pointer(uintptr(pointerOfa) + uintptr(pointerSize) + uintptr(intSize)))

        fmt.Printf("the internalCapLength: %d, cap(a): %d\n", *internalCapPointer, cap(a))
        fmt.Printf("the internalLengthPointer: %d, len(c): %d\n", *internalLengthPointer, len(a))

        *internalLengthPointer = 10

        fmt.Println("after modify")
        fmt.Println(a)

}

output:

[200 2]
the internalCapLength: 20, cap(a): 20
the internalLengthPointer: 2, len(c): 2
after modify
[200 2 1 2 3 4 5 6 7 8]

kele1997 avatar Jan 06 '21 09:01 kele1997

@kele1997 嗯,这一串操作好 6 ~

geektutu avatar Jan 11 '21 12:01 geektutu

大佬这个系列写的挺好的,通过测试对性能有了直观的感受,多多交流

wsqyouth avatar Jan 11 '21 15:01 wsqyouth

@wsqyouth Thanks♪(・ω・)ノ,多交流~

geektutu avatar Jan 11 '21 17:01 geektutu

大佬,文章什么时候再更新?

zhangxuanru avatar Jan 18 '21 08:01 zhangxuanru

学到了,学到了

rankingcong avatar Jan 27 '21 06:01 rankingcong

全部看完了,学到了很多

UOvOU avatar Nov 17 '21 06:11 UOvOU

传参时拷贝了新的切片,因此当新切片的长度发生改变时,原切片并不会发生改变。

这里应该是切片容量发生改变时,才会分配新的内存空间,导致函数体中的切片底层指针指向新的空间。如果容量不变,foo函数的作用效果是可以体现在原切片中的。

SericaLaw avatar Dec 23 '21 10:12 SericaLaw

看完了,受益匪浅

dablelv avatar Mar 07 '22 12:03 dablelv

谢谢大佬 看完了 收获颇多

WAYLON avatar Apr 02 '22 10:04 WAYLON

func foo(a []int) { a = append(a, 1, 2, 3, 4, 5, 6, 7, 8) a[0] = 200 }

func main() { a := []int{1, 2} foo(a) fmt.Println(a) }

如果不append的话会影响a的,a是引用类型。归根到底是slice是引用类型,当 len > cap 后会分配新的内存。

tiankongnanhai avatar May 13 '22 07:05 tiankongnanhai

博主不更新了吗

JYGQAQ avatar Jun 03 '22 14:06 JYGQAQ

看完,求更

js1219 avatar Aug 05 '22 09:08 js1219

大佬牛逼,看完收获颇多

JJliangxunfu avatar Aug 12 '22 09:08 JJliangxunfu

切片是引用传递,在未扩容的情况下,修改原切片长度内的元素会修改原数组,但是一旦扩容后,就类似一个副本,怎么修改都不影响原切片

Leviathangk avatar Apr 27 '23 07:04 Leviathangk

看完了,学到了好多,谢谢兔兔!

LydiaCai1203 avatar Oct 03 '23 11:10 LydiaCai1203