切片(slice)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zzuchengming/article/details/88617493

slice 数据结构

type slice struct {
    array unsafe.Pointer  //指向底层数组的指针
    len   int //切片中元素个数
    cap   int //切片总容量
}

golang 源码

  • 基于数组或者slice生成一个slice的时候,新的slice和原来数组/slice 的底层数组是同一个

  • 基于数组或者slice生成slice的cap=原来对应的数组长度-现在slice的第一个元素对应的索引

  • slice 作为参数传递的是 副本,但是对应的数组的指针不变

package main

import "fmt"

var arr = []int{1,2,3,4,5}

func testSliceAsParam1(s1 []int)  {
	s1[0] = 5  //扩容前修改,影响传入slice,因为s1指向底层数组没变,但是底层数组元素值变了
	return
}

func testSliceAsParam2(s1 []int)  {
	s1 = append(s1, 6) //扩容后修改,不影响传入slice,因为扩容后产生了新的底层数组,函数体内的
	                   //副本s1,其指向底层数组的指针已经改变,指向新生成的底层数组,此时修改只
	                  //是修改新的底层数组,原s1指向的底层数组元素值没变
	s1[0] = 9
	return
}

func main()  {
	s1 := arr[:5]
	fmt.Println("before1: ")
	fmt.Println(s1)// [1 2 3 4 5]
	testSliceAsParam1(s1)
	fmt.Println("after1: ")
	fmt.Println(s1)// [5 2 3 4 5]
	testSliceAsParam2(s1)
	fmt.Println("after2: ")
	fmt.Println(s1) //[5 2 3 4 5]
}
  • 扩容规则:
    在一般的情况下,你可以简单地认为新切片的容量(以下简称新容量)将会是原切片容量(以下简称原容量)的 2 倍。
    但是,当原切片的长度(以下简称原长度)大于或等于1024时,Go 语言将会以原容量的1.25倍作为新容量的基准(以下新容量基准)

slice 数组指针什么情况下会发生变化?

确切地说,一个切片的底层数组永远不会被替换。为什么?虽然在扩容的时候 Go 语言一定会生成新的底层数组,但是它也同时生成了新的切片。它是把新的切片作为了新底层数组的窗口,而没有对原切片及其底层数组做任何改动。
测试:
append扩容后,确实产生了新的底层数组,也生成了新的切片;但当你用原切片作为append的接收者,因为‘=’是值传递,你看到切片的地址是没有改变的。

package main

import "fmt"

var arr = []int{1,2,3,4,5}

func main()  {
	s1 := arr[:5]
	fmt.Printf("%p", &s1) //0xc0420023e0
	fmt.Println()
	s2 := append(s1, 6) //确实产生了新的底层数组和新的切片
	fmt.Printf("%p", &s2) //0xc042002420
	fmt.Println()
	s1 = s2  //值传递,这里等价于s1 = append(s1,6)
	fmt.Printf("%p", &s1) //0xc0420023e0
}

猜你喜欢

转载自blog.csdn.net/zzuchengming/article/details/88617493