专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

我可能并不会使用golang slice

使用了很多的slice,最近再准备面试的时候,才发现,自己对他是一知半解的,倘若问几个比较戏剧性的问题,发现自己还是第一时间无法给出正确答案的,所以今天,系统性的整理一下有关slice的一些知识点。

package main

import (
    "fmt"
)

func main() {

    a := make([]int, 10)
    a = append(a, 1, 2)
    FuncA(a)
    fmt.Println(a)
    a[0] = 44
    FuncA(a)
    fmt.Println(a)
}

func FuncA(b []int) {
    b[0] =19
}

上面的示例输出是:

[19 0 0 0 0 0 0 0 0 0 1 2]
[19 0 0 0 0 0 0 0 0 0 1 2]

从输出看,一般情况下,会有两个疑问:

  • 我实例化一个切片之后,append两个元素,为什么不是切片的第一、二个元素
  • 我一个值传递,调用FuncA,为啥可以在它函数体内,修改我切片的内容

有问题再细细看,就明白了切片的原理, 首先看第一个问题。实例化切片后,进行append操作

当我们在执行make([]int, 10)这样的语句时,底层调用的是makeslice函数。

    0x002f 00047 (main2.go:10)  LEAQ    type.int(SB), AX
    0x0036 00054 (main2.go:10)  PCDATA  $2, $0
    0x0036 00054 (main2.go:10)  MOVQ    AX, (SP)
    0x003a 00058 (main2.go:10)  MOVQ    $10, 8(SP)
    0x0043 00067 (main2.go:10)  MOVQ    $10, 16(SP)
    0x004c 00076 (main2.go:10)  CALL    runtime.makeslice(SB)

makeslice函数的三个参数为,type.int, 10, 10 后面两个参数是slice的长度和底层数组的长度,即slice容量。

func makeslice(et *_type, len, cap int) unsafe.Pointer {
    mem, overflow := math.MulUintptr(et.size, uintptr(cap))
    if overflow || mem > maxAlloc || len < 0 || len > cap {
        // NOTE: Produce a 'len out of range' error instead of a
        // 'cap out of range' error when someone does make([]T, bignumber).
        // 'cap out of range' is true too, but since the cap is only being
        // supplied implicitly, saying len is clearer.
        // See golang.org/issue/4085.
        mem, overflow := math.MulUintptr(et.size, uintptr(len))
        if overflow || mem > maxAlloc || len < 0 {
            panicmakeslicelen()
        }
        panicmakeslicecap()
    }

    return mallocgc(mem, et, true)
}

makeslice首先做的是检查栈溢出,如果溢出,直接panic,否则,调用mallocgc进行资源分配,看一下该函数的原型

func mallocgc(size uintptr, typ *_type, needzero bool) unsafe.Pointer 

分配一个大小为size的对象。小对象是从per-P缓存的空闲列表中分配的。大对象(> 32 kB)是从堆中直接分配的。 mallocgc的参数为,要分配的内存大小:mem= type.int.size * cap。第二个参数为元素类型。第三个参数是个bool值,如果needzero为true,则返回范围的内存将归零。该方法的返回值是分配的资源的地址。 上面的动作完成之后,就是变量赋值了

    0x004c 00076 (main2.go:10)  CALL    runtime.makeslice(SB)
    0x0051 00081 (main2.go:10)  PCDATA  $2, $1
    0x0051 00081 (main2.go:10)  MOVQ    24(SP), AX
    0x0056 00086 (main2.go:10)  MOVQ    AX, "".a+112(SP)
    0x005b 00091 (main2.go:10)  MOVQ    $10, "".a+120(SP)
    0x0064 00100 (main2.go:10)  MOVQ    $10, "".a+128(SP)

该赋值动作涵盖三部分,第一部分就是分配的内存地址,第二部分就是slice长度,第三部分就是底层数组的长度,这也印证了,slice的数据类型由三部分组成。

在调用函数append进行增加元素的时候,我们看看slice又做了哪些操作

    0x0070 00112 (main2.go:11)  JMP 114
    0x0072 00114 (main2.go:11)  PCDATA  $2, $2
    0x0072 00114 (main2.go:11)  LEAQ    type.int(SB), CX
    0x0079 00121 (main2.go:11)  PCDATA  $2, $1
    0x0079 00121 (main2.go:11)  MOVQ    CX, (SP)
    0x007d 00125 (main2.go:11)  PCDATA  $2, $0
    0x007d 00125 (main2.go:11)  MOVQ    AX, 8(SP)
    0x0082 00130 (main2.go:11)  MOVQ    $10, 16(SP)
    0x008b 00139 (main2.go:11)  MOVQ    $10, 24(SP)
    0x0094 00148 (main2.go:11)  MOVQ    $12, 32(SP)
    0x009d 00157 (main2.go:11)  CALL    runtime.growslice(SB)
    0x00a2 00162 (main2.go:11)  PCDATA  $2, $1
    0x00a2 00162 (main2.go:11)  MOVQ    40(SP), AX
    0x00a7 00167 (main2.go:11)  MOVQ    48(SP), CX
    0x00ac 00172 (main2.go:11)  MOVQ    56(SP), DX
    0x00b1 00177 (main2.go:11)  ADDQ    $2, CX
    0x00b5 00181 (main2.go:11)  JMP 183
    0x00b7 00183 (main2.go:11)  MOVQ    $1, 80(AX)
    0x00bf 00191 (main2.go:11)  MOVQ    $2, 88(AX)
    0x00c7 00199 (main2.go:11)  PCDATA  $0, $1
    0x00c7 00199 (main2.go:11)  MOVQ    AX, "".a+112(SP)
    0x00cc 00204 (main2.go:11)  MOVQ    CX, "".a+120(SP)
    0x00d1 00209 (main2.go:11)  MOVQ    DX, "".a+128(SP)

我们发现,在增加元素之前,先对slice进行了一次扩容,调用了runtime.growslice函数,该函数原型是

func growslice(et *_type, old slice, cap int) slice

该函数提供了三个参数,第一个参数是元素类型type.int,第二个参数是扩容前原始slice,第三个参数是新slice的容量,该例子中是12.该函数调用完成后,返回新的slice. 新的slice进行了一个长度增加2的动作后,把新的元素放到了slice中数组中是放在偏移量为80和88的地址里的。

    0x00b7 00183 (main2.go:11)  MOVQ    $1, 80(AX)
    0x00bf 00191 (main2.go:11)  MOVQ    $2, 88(AX)

这也就是第一个问题,其将数据是追加到数组的最后的。

所有动作完成之后,又重新进行了一次赋值操作

    0x00c7 00199 (main2.go:11)  MOVQ    AX, "".a+112(SP)
    0x00cc 00204 (main2.go:11)  MOVQ    CX, "".a+120(SP)
    0x00d1 00209 (main2.go:11)  MOVQ    DX, "".a+128(SP)

之后就开始了FuncA函数的调用

    0x00d9 00217 (main2.go:12)  MOVQ    AX, (SP)
    0x00dd 00221 (main2.go:12)  MOVQ    CX, 8(SP)
    0x00e2 00226 (main2.go:12)  MOVQ    DX, 16(SP)
    0x00e7 00231 (main2.go:12)  CALL    "".FuncA(SB)

此次调用非常有意思的是,参数上,将a slice的底层切片结构提供给了FuncA,这也就印证了第二个问题,为啥我在FuncA中对slice的元素修改,可以影响到我的原始slice. 再看一下FuncA的函数调用情况

"".FuncA STEXT nosplit size=50 args=0x18 locals=0x8
    0x0000 00000 (main2.go:19)  TEXT    "".FuncA(SB), NOSPLIT|ABIInternal, $8-24
    0x0000 00000 (main2.go:19)  SUBQ    $8, SP
    0x0004 00004 (main2.go:19)  MOVQ    BP, (SP)
    0x0008 00008 (main2.go:19)  LEAQ    (SP), BP
    0x000c 00012 (main2.go:19)  FUNCDATA    $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
    0x000c 00012 (main2.go:19)  FUNCDATA    $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
    0x000c 00012 (main2.go:19)  FUNCDATA    $3, gclocals·9fb7f0986f647f17cb53dda1484e0f7a(SB)
    0x000c 00012 (main2.go:20)  PCDATA  $2, $1
    0x000c 00012 (main2.go:20)  PCDATA  $0, $0
    0x000c 00012 (main2.go:20)  MOVQ    "".b+16(SP), AX
    0x0011 00017 (main2.go:20)  PCDATA  $0, $1
    0x0011 00017 (main2.go:20)  CMPQ    "".b+24(SP), $0
    0x0017 00023 (main2.go:20)  JHI 27
    0x0019 00025 (main2.go:20)  JMP 43
    0x001b 00027 (main2.go:20)  PCDATA  $2, $0
    0x001b 00027 (main2.go:20)  MOVQ    $19, (AX)
    0x0022 00034 (main2.go:21)  MOVQ    (SP), BP
    0x0026 00038 (main2.go:21)  ADDQ    $8, SP
    0x002a 00042 (main2.go:21)  RET
    0x002b 00043 (main2.go:20)  CALL    runtime.panicindex(SB)
    0x0030 00048 (main2.go:20)  UNDEF
    0x0000 48 83 ec 08 48 89 2c 24 48 8d 2c 24 48 8b 44 24  H...H.,$H.,$H.D$
    0x0010 10 48 83 7c 24 18 00 77 02 eb 10 48 c7 00 13 00  .H.|$..w...H....
    0x0020 00 00 48 8b 2c 24 48 83 c4 08 c3 e8 00 00 00 00  ..H.,$H.........
    0x0030 0f 0b                                            ..
    rel 44+4 t=8 runtime.panicindex+0

首先进行一个索引的比较,如果是0,则执行赋值。然后收回资源返回。

再来看看几个有意思的示例:

package main

import (
    "fmt"
)

func main() {
    a := make([]int, 10)
    FuncA(a)
    fmt.Println(a)
}

func FuncA(b []int) {
    b = append(b, 1, 2)
}

还是相同的例子,只不过,我们在FuncA中执行的是append操作,而不是索引的赋值操作。首先看一下该程序的输出:

[0 0 0 0 0 0 0 0 0 0]

发现么,原始slice的值并没有修改,这特么的为啥,不是可以修改么?

    0x0021 00033 (main2.go:11)  MOVQ    "".b+96(SP), AX
    0x0026 00038 (main2.go:11)  LEAQ    2(AX), CX
    0x002a 00042 (main2.go:11)  PCDATA  $2, $1
    0x002a 00042 (main2.go:11)  MOVQ    "".b+88(SP), DX
    0x002f 00047 (main2.go:11)  PCDATA  $0, $1
    0x002f 00047 (main2.go:11)  MOVQ    "".b+104(SP), BX
    0x0034 00052 (main2.go:11)  CMPQ    CX, BX
    0x0037 00055 (main2.go:11)  JLS 59
    0x0039 00057 (main2.go:11)  JMP 103
    0x003b 00059 (main2.go:11)  PCDATA  $2, $-2
    0x003b 00059 (main2.go:11)  PCDATA  $0, $-2
    0x003b 00059 (main2.go:11)  JMP 61
    0x003d 00061 (main2.go:11)  PCDATA  $2, $1
    0x003d 00061 (main2.go:11)  PCDATA  $0, $1
    0x003d 00061 (main2.go:11)  MOVQ    $1, (DX)(AX*8)
    0x0045 00069 (main2.go:11)  MOVQ    $2, 8(DX)(AX*8)
    0x004e 00078 (main2.go:11)  PCDATA  $2, $0
    0x004e 00078 (main2.go:11)  MOVQ    DX, "".b+88(SP)
    0x0053 00083 (main2.go:11)  MOVQ    CX, "".b+96(SP)
    0x0058 00088 (main2.go:11)  MOVQ    BX, "".b+104(SP)
    0x005d 00093 (main2.go:12)  MOVQ    72(SP), BP
    0x0062 00098 (main2.go:12)  ADDQ    $80, SP
    0x0066 00102 (main2.go:12)  RET
    0x0067 00103 (main2.go:11)  PCDATA  $2, $1
    0x0067 00103 (main2.go:11)  MOVQ    AX, ""..autotmp_1+64(SP)
    0x006c 00108 (main2.go:11)  PCDATA  $2, $2
    0x006c 00108 (main2.go:11)  LEAQ    type.int(SB), SI
    0x0073 00115 (main2.go:11)  PCDATA  $2, $1
    0x0073 00115 (main2.go:11)  MOVQ    SI, (SP)
    0x0077 00119 (main2.go:11)  PCDATA  $2, $0
    0x0077 00119 (main2.go:11)  MOVQ    DX, 8(SP)
    0x007c 00124 (main2.go:11)  MOVQ    AX, 16(SP)
    0x0081 00129 (main2.go:11)  MOVQ    BX, 24(SP)
    0x0086 00134 (main2.go:11)  MOVQ    CX, 32(SP)
    0x008b 00139 (main2.go:11)  CALL    runtime.growslice(SB)
    0x0090 00144 (main2.go:11)  PCDATA  $2, $1
    0x0090 00144 (main2.go:11)  MOVQ    40(SP), DX
    0x0095 00149 (main2.go:11)  MOVQ    48(SP), AX
    0x009a 00154 (main2.go:11)  MOVQ    56(SP), BX
    0x009f 00159 (main2.go:11)  LEAQ    2(AX), CX
    0x00a3 00163 (main2.go:11)  MOVQ    ""..autotmp_1+64(SP), AX
    0x00a8 00168 (main2.go:11)  JMP 61

首先判断是否需要growslice,如果需要,先进行growslice操作,之后再进行元素的追加,元素的追加也是很简单的,就是寄存器的地址运算,然后将值移动到指定的。

0x003d 00061 (main2.go:11)  MOVQ    $1, (DX)(AX*8)
    0x0045 00069 (main2.go:11)  MOVQ    $2, 8(DX)(AX*8)

完成后进行赋值操作

    0x004e 00078 (main2.go:11)  MOVQ    DX, "".b+88(SP)
    0x0053 00083 (main2.go:11)  MOVQ    CX, "".b+96(SP)
    0x0058 00088 (main2.go:11)  MOVQ    BX, "".b+104(SP)

上面的这个函数的操作,是在扩容的情况下,新分配的底层数组DX寄存器,+AX寄存器的偏移后,进行的赋值,此时b作为独立的slice,值是放到该slice中的底层数组中的。至于FuncA函数返回后,对于原始的slice就并没有产生什么影响。 下面的示例更能说明情况

package main

import (
    "fmt"
)

func main() {
    a := make([]int, 10, 10)
    b := a
    b = append(b ,1,2)
    b[0] =19
    fmt.Println(a)
}

结果是[0 0 0 0 0 0 0 0 0 0].append在操作时,会判断,是否需要扩容,就是其cap(底层数组的长度)是否够用.如果不够用,进行了扩容的操作,那么会分配一个新的基础数组,那么b和a就没了联系,并不会再共用同一个底层数组,但是如果并没有进行扩容操作,底层数组够用,那么其实他们还是会共用同一个底层数组,只不过其所看到的内容不同罢了。

上面的例子还是比较极端的情况,再看一个不用进行growslice操作的情况,看看会不会有什么影响;

package main

func main() {
    a := make([]int, 10, 20)
    FuncA(a)
}

func FuncA(b []int) {
    b = append(b, 1, 2)
}

首先看一下其输出情况:

[0 0 0 0 0 0 0 0 0 0]

奇怪,我分配了那么大cap的slice,在FuncA中,并不需要进行growslice操作,为什么还是没有影响到原始的slice呢 .我们有理由怀疑,这一切的原因都是append函数,导致的,那么append到底进行了什么操作呢?

append函数是golang内建的函数,具体声明为:

// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//  slice = append(slice, elem1, elem2)
//  slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
//  slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type

就是说,内建的append函数将元素追加到slice尾部,如果其有足够的空间,因此可以元素追加到目标切片中,以容纳新元素。 如果没有,将分配一个新的基础数组。 追加返回更新的切片。 因此,有必要将append的结果存储在通常包含切片本身的变量中,

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)

具体到,更简单的操作,来看一下:

package main

import (
    "fmt"
)

func main() {
    a := make([]int, 10, 20)
    b := a
    b = append(b ,1,2)
    fmt.Println(a)
}

其结果一样的,输出是[0 0 0 0 0 0 0 0 0 0]

但是通过b可以实现对a slice底层数组的索引修改,比如

package main

import (
    "fmt"
)

func main() {
    a := make([]int, 10, 20)
    b := a
    b = append(b ,1,2)
    b[0] =19
    fmt.Println(a)
}

输出是:[19 0 0 0 0 0 0 0 0 0].其底层数组中,指定是同一个数组,只是append的操作,并不影响a slice罢了。 a和b仅仅共用其相同的部分,其追加的东西并不会共享。

还有一种比较有意思的情况

package main

import (
    "fmt"
)

func main() {
    a := make([]int, 10, 10)
    FuncA(a)
    fmt.Println(a)
}

func FuncA(b []int) {
    for i, v :=range b {
        v += 1
    }
}

其结果输出是:[0 0 0 0 0 0 0 0 0 0]

"".FuncA STEXT nosplit size=139 args=0x18 locals=0x40
    0x0000 00000 (main2.go:9)   TEXT    "".FuncA(SB), NOSPLIT|ABIInternal, $64-24
    0x0000 00000 (main2.go:9)   SUBQ    $64, SP
    0x0004 00004 (main2.go:9)   MOVQ    BP, 56(SP)
    0x0009 00009 (main2.go:9)   LEAQ    56(SP), BP
    0x000e 00014 (main2.go:9)   FUNCDATA    $0, gclocals·2d7c1615616d4cf40d01b3385155ed6e(SB)
    0x000e 00014 (main2.go:9)   FUNCDATA    $1, gclocals·6d81f9fc90b2254ac2f1067a7bf2c67c(SB)
    0x000e 00014 (main2.go:9)   FUNCDATA    $3, gclocals·db688afbc90e26183a53c9ad23b80c29(SB)
    0x000e 00014 (main2.go:10)  PCDATA  $2, $0
    0x000e 00014 (main2.go:10)  PCDATA  $0, $0
    0x000e 00014 (main2.go:10)  MOVQ    "".b+88(SP), AX
    0x0013 00019 (main2.go:10)  MOVQ    "".b+80(SP), CX
    0x0018 00024 (main2.go:10)  PCDATA  $2, $1
    0x0018 00024 (main2.go:10)  PCDATA  $0, $1
    0x0018 00024 (main2.go:10)  MOVQ    "".b+72(SP), DX
    0x001d 00029 (main2.go:10)  PCDATA  $2, $0
    0x001d 00029 (main2.go:10)  PCDATA  $0, $2
    0x001d 00029 (main2.go:10)  MOVQ    DX, ""..autotmp_2+32(SP)
    0x0022 00034 (main2.go:10)  MOVQ    CX, ""..autotmp_2+40(SP)
    0x0027 00039 (main2.go:10)  MOVQ    AX, ""..autotmp_2+48(SP)
    0x002c 00044 (main2.go:10)  MOVQ    $0, ""..autotmp_3+24(SP)
    0x0035 00053 (main2.go:10)  MOVQ    ""..autotmp_2+40(SP), AX
    0x003a 00058 (main2.go:10)  MOVQ    AX, ""..autotmp_4+16(SP)
    0x003f 00063 (main2.go:10)  JMP 65
    0x0041 00065 (main2.go:10)  MOVQ    ""..autotmp_4+16(SP), AX
    0x0046 00070 (main2.go:10)  CMPQ    ""..autotmp_3+24(SP), AX
    0x004b 00075 (main2.go:10)  JLT 79
    0x004d 00077 (main2.go:10)  JMP 129
    0x004f 00079 (main2.go:10)  MOVQ    ""..autotmp_3+24(SP), AX
    0x0054 00084 (main2.go:10)  SHLQ    $3, AX
    0x0058 00088 (main2.go:10)  PCDATA  $2, $2
    0x0058 00088 (main2.go:10)  ADDQ    ""..autotmp_2+32(SP), AX
    0x005d 00093 (main2.go:10)  PCDATA  $2, $0
    0x005d 00093 (main2.go:10)  MOVQ    (AX), AX
    0x0060 00096 (main2.go:10)  MOVQ    AX, ""..autotmp_5+8(SP)
    0x0065 00101 (main2.go:10)  MOVQ    AX, "".v(SP)
    0x0069 00105 (main2.go:11)  INCQ    AX
    0x006c 00108 (main2.go:11)  MOVQ    AX, "".v(SP)
    0x0070 00112 (main2.go:11)  JMP 114
    0x0072 00114 (main2.go:10)  MOVQ    ""..autotmp_3+24(SP), AX
    0x0077 00119 (main2.go:10)  INCQ    AX
    0x007a 00122 (main2.go:10)  MOVQ    AX, ""..autotmp_3+24(SP)
    0x007f 00127 (main2.go:10)  JMP 65
    0x0081 00129 (<unknown line number>)    PCDATA  $0, $1
    0x0081 00129 (<unknown line number>)    MOVQ    56(SP), BP
    0x0086 00134 (<unknown line number>)    ADDQ    $64, SP
    0x008a 00138 (<unknown line number>)    RET
    0x0000 48 83 ec 40 48 89 6c 24 38 48 8d 6c 24 38 48 8b  H..@H.l$8H.l$8H.
    0x0010 44 24 58 48 8b 4c 24 50 48 8b 54 24 48 48 89 54  D$XH.L$PH.T$HH.T
    0x0020 24 20 48 89 4c 24 28 48 89 44 24 30 48 c7 44 24  $ H.L$(H.D$0H.D$
    0x0030 18 00 00 00 00 48 8b 44 24 28 48 89 44 24 10 eb  .....H.D$(H.D$..
    0x0040 00 48 8b 44 24 10 48 39 44 24 18 7c 02 eb 32 48  .H.D$.H9D$.|..2H
    0x0050 8b 44 24 18 48 c1 e0 03 48 03 44 24 20 48 8b 00  .D$.H...H.D$ H..
    0x0060 48 89 44 24 08 48 89 04 24 48 ff c0 48 89 04 24  H.D$.H..$H..H..$
    0x0070 eb 00 48 8b 44 24 18 48 ff c0 48 89 44 24 18 eb  ..H.D$.H..H.D$..
    0x0080 c0 48 8b 6c 24 38 48 83 c4 40 c3                 .H.l$8H..@.

如果改成如下形式,结果就不一样了:

package main

import (
    "fmt"
)

func main() {
    a := make([]int, 10, 10)
    FuncA(a)
    fmt.Println(a)
}

func FuncA(b []int) {
    for i, _ :=range b {
        b[i] += 1
    }
}

其结果输出是:[1 1 1 1 1 1 1 1 1 1]

"".FuncA STEXT nosplit size=135 args=0x18 locals=0x20
    0x0000 00000 (main2.go:9)   TEXT    "".FuncA(SB), NOSPLIT|ABIInternal, $32-24
    0x0000 00000 (main2.go:9)   SUBQ    $32, SP
    0x0004 00004 (main2.go:9)   MOVQ    BP, 24(SP)
    0x0009 00009 (main2.go:9)   LEAQ    24(SP), BP
    0x000e 00014 (main2.go:9)   FUNCDATA    $0, gclocals·1a65e721a2ccc325b382662e7ffee780(SB)
    0x000e 00014 (main2.go:9)   FUNCDATA    $1, gclocals·69c1753bd5f81501d95132d08af04464(SB)
    0x000e 00014 (main2.go:9)   FUNCDATA    $3, gclocals·7cae486b9f11463edb9d0e91d30ff0f7(SB)
    0x000e 00014 (main2.go:10)  PCDATA  $2, $0
    0x000e 00014 (main2.go:10)  PCDATA  $0, $0
    0x000e 00014 (main2.go:10)  MOVQ    $0, ""..autotmp_2+16(SP)
    0x0017 00023 (main2.go:10)  MOVQ    "".b+48(SP), AX
    0x001c 00028 (main2.go:10)  MOVQ    AX, ""..autotmp_3+8(SP)
    0x0021 00033 (main2.go:10)  JMP 35
    0x0023 00035 (main2.go:10)  MOVQ    ""..autotmp_3+8(SP), AX
    0x0028 00040 (main2.go:10)  CMPQ    ""..autotmp_2+16(SP), AX
    0x002d 00045 (main2.go:10)  JLT 49
    0x002f 00047 (main2.go:10)  JMP 118
    0x0031 00049 (main2.go:10)  MOVQ    ""..autotmp_2+16(SP), AX
    0x0036 00054 (main2.go:10)  MOVQ    AX, "".i(SP)
    0x003a 00058 (main2.go:11)  PCDATA  $2, $1
    0x003a 00058 (main2.go:11)  MOVQ    "".b+40(SP), CX
    0x003f 00063 (main2.go:11)  CMPQ    "".b+48(SP), AX
    0x0044 00068 (main2.go:11)  JHI 72
    0x0046 00070 (main2.go:11)  JMP 116
    0x0048 00072 (main2.go:11)  PCDATA  $2, $0
    0x0048 00072 (main2.go:11)  MOVQ    (CX)(AX*8), CX
    0x004c 00076 (main2.go:11)  PCDATA  $2, $2
    0x004c 00076 (main2.go:11)  MOVQ    "".b+40(SP), DX
    0x0051 00081 (main2.go:11)  INCQ    CX
    0x0054 00084 (main2.go:11)  CMPQ    "".b+48(SP), AX
    0x0059 00089 (main2.go:11)  JHI 93
    0x005b 00091 (main2.go:11)  JMP 114
    0x005d 00093 (main2.go:11)  PCDATA  $2, $0
    0x005d 00093 (main2.go:11)  MOVQ    CX, (DX)(AX*8)
    0x0061 00097 (main2.go:11)  JMP 99
    0x0063 00099 (main2.go:10)  MOVQ    ""..autotmp_2+16(SP), AX
    0x0068 00104 (main2.go:10)  INCQ    AX
    0x006b 00107 (main2.go:10)  MOVQ    AX, ""..autotmp_2+16(SP)
    0x0070 00112 (main2.go:10)  JMP 35
    0x0072 00114 (main2.go:11)  PCDATA  $2, $-2
    0x0072 00114 (main2.go:11)  PCDATA  $0, $-2
    0x0072 00114 (main2.go:11)  JMP 128
    0x0074 00116 (main2.go:11)  JMP 128
    0x0076 00118 (<unknown line number>)    MOVQ    24(SP), BP
    0x007b 00123 (<unknown line number>)    ADDQ    $32, SP
    0x007f 00127 (<unknown line number>)    RET
    0x0080 00128 (main2.go:11)  PCDATA  $2, $0
    0x0080 00128 (main2.go:11)  PCDATA  $0, $1
    0x0080 00128 (main2.go:11)  CALL    runtime.panicindex(SB)
    0x0085 00133 (main2.go:11)  UNDEF
    0x0000 48 83 ec 20 48 89 6c 24 18 48 8d 6c 24 18 48 c7  H.. H.l$.H.l$.H.
    0x0010 44 24 10 00 00 00 00 48 8b 44 24 30 48 89 44 24  D$.....H.D$0H.D$
    0x0020 08 eb 00 48 8b 44 24 08 48 39 44 24 10 7c 02 eb  ...H.D$.H9D$.|..
    0x0030 45 48 8b 44 24 10 48 89 04 24 48 8b 4c 24 28 48  EH.D$.H..$H.L$(H
    0x0040 39 44 24 30 77 02 eb 2c 48 8b 0c c1 48 8b 54 24  9D$0w..,H...H.T$
    0x0050 28 48 ff c1 48 39 44 24 30 77 02 eb 15 48 89 0c  (H..H9D$0w...H..
    0x0060 c2 eb 00 48 8b 44 24 10 48 ff c0 48 89 44 24 10  ...H.D$.H..H.D$.
    0x0070 eb b1 eb 0c eb 0a 48 8b 6c 24 18 48 83 c4 20 c3  ......H.l$.H.. .
    0x0080 e8 00 00 00 00 0f 0b                             .......
    rel 129+4 t=8 runtime.panicindex+0

总结:

1、append操作在无需扩容的情况下,新的slice和老slice共用底层的数组,通过索引对新的slice的修改,会影响到老的slice

2、所谓共用底层数组,仅仅共用其公共部分,至于特有的部分,各方也是无法修改和查看的

3、在for循环遍历slice时,value是一次值copy,对其的修改,并不会影响到slice底层的数据

本系列文章:

有任何问题,欢迎留言

文章永久链接:https://tech.souyunku.com/41022

未经允许不得转载:搜云库技术团队 » 我可能并不会使用golang slice

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们