golang- slice解析

发布时间 2023-11-12 04:21:06作者: 我才不是老家伙

Slice(切片)

定义

切片的概念在python中也存在这个概念,在go中,切片是对数组的一个连续片段的引用,所以切片是一个引用类型,它的内部结构包含地址长度容量
版本号:go version go1.20 darwin/arm64

runtime/slice.go

type slice struct {
	array unsafe.Pointer 
	len   int
	cap   int
}

其中:

  • array是底层数组指针,指向该切片的第一个元素
  • len是切片的长度
  • cap是切片的容量

变量声明

主要是使用make关键字去进行切片的创建

// make([]T, len, cap)
// len: 长度
// cap: 容量
// T: 类型
var s1 []int = make([]int, 5, 10)

var s2 []int =make([]int, 5)

容量未声明,则与长度相等

s1在初始化后,长度为5,容量为10,值为[0,0,0,0,0]

s2在初始化后,长度为5,容量为5,值为[0,0,0,0,0]

扩容

扩容的前提是当前切片容量不足以容纳增加后的数据。

相关函数:

runtime/slice.go/growslice


	newcap := oldCap
	doublecap := newcap + newcap
	if newLen > doublecap {
		newcap = newLen
	} else {
		const threshold = 256
		if oldCap < threshold {
			newcap = doublecap
		} else {
			// Check 0 < newcap to detect overflow
			// and prevent an infinite loop.
			for 0 < newcap && newcap < newLen {
				// Transition from growing 2x for small slices
				// to growing 1.25x for large slices. This formula
				// gives a smooth-ish transition between the two.
				newcap += (newcap + 3*threshold) / 4
			}
			// Set newcap to the requested cap when
			// the newcap calculation overflowed.
			if newcap <= 0 {
				newcap = newLen
			}
		}
	}
  • 如果当前容量扩充两倍后,仍然无法容纳新的数据,那么新的容量就为新的长度oldlen+num

  • 扩充两倍后,可以容纳新的数据,再判断oldlen是否>=1024,大于则扩充1.25倍,小于则扩充两倍

函数传参

切片作为函数参数传递时,传递的是切片的引用,所以在函数内部修改切片的值,会影响到函数外部的切片值。

简单来说,切片a作为参数传递时,编译器会根据a的地址、长度、容量,创建一个新的切片b,然后将b作为参数传递给函数,所以在函数内部修改切片的值,a和b指向的底层数组x一致。修改b的元素,本质上修改底层数组的数据,所以a的值也会发生变化。

但如果切片b发生扩容,情况又不一样

更新切片

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

func UpdateSlice(b []int) {
    b[0] = 100
}

因为只是更新切片数据,修改b的元素,本质上修改底层数组的数据,a和b都还是指向同一块内存空间,所以a的值也会发生变化。

扩容切片

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

func GrowSlice(b []int) {
    b = append(b, []int{1, 2, 3, 4, 5, 6, 7, 8, 9}...)
    fmt.Println(b)
}

b进行了append操作,容量不足,发生扩容,编译器重新分配一块合适的内存空间,并修改了b的指向,此时再去修改b的值,不会影响到a的值。