golang slice原理

Slice结构体定义

	//slice 结构体定义如下
	type slice struct {
    
    
		array unsafe.Pointer			//指向数据的首地址指针      8字节
		len   int					    //长度				  4||8字节	
		cap   int						//容量				  4||8字节
	}

make Slice

	// 初始化 slice
	s1 := make([]int, 3, 5)
	fmt.Printf("s1: s1 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s1, unsafe.Pointer(&s1[0]), len(s1), cap(s1), s1)
    // 输出  s1: s1 address:0xc000127e60, data address: 0xc000119b00, len: 3, cap: 5,[0 0 0]

	// 添加元素, 不扩容, data address不变(不重新分配内存)
	s1 = append(s1, 1, 2)
	fmt.Printf("s1: s1 address:%p, data address: %p, len: %v, cap: %v, %v\n", &s1, unsafe.Pointer(&s1[0]), len(s1), cap(s1), s1)
    // 输出  s1: s1 address:0xc000127e60, data address: 0xc000119b00, len: 5, cap: 5, [0 0 0 1 2]
	
	// 添加元素, 扩容, data address变更(重新分配内存)
	s1 = append(s1, 1, 2, 3, 4)
	fmt.Printf("s1: s1 address:%p, data address: %p, len: %v, cap: %v, %v\n", &s1, unsafe.Pointer(&s1[0]), len(s1), cap(s1), s1)
	// 输出 s1: s1 address:0xc000127e60, data address: 0xc00014dbd0, len: 9, cap: 10, [0 0 0 1 2 1 2 3 4]
	
	arr := [10]int{
    
    0,1,2,3,4,5,6,7,8,9}
	s3 := arr[2:6]
	fmt.Printf("arr: arr address:%p, data address: %p, len: %v, cap: %v,%v\n", &arr, &arr[2], len(arr), cap(arr), arr)
	// 输出 arr: arr address:0xc00014dc20, data address: 0xc00014dc30, len: 10, cap: 10,[0 1 2 3 4 5 6 7 8 9]

	// 从数组索引出来的切片 s1[start:end], len=end-start, cap=len(arr) - start, 数据地址为数组第start元素地址
	s2 := arr[2:5]
	fmt.Printf("s2: s2 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s2, &s2[0], len(s2), cap(s2), s2)
	fmt.Printf("s3: s3 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s3, &s3[0], len(s3), cap(s3), s3)
	//输出 s2: s2 address:0xc000127f00, data address: 0xc00014dc30, len: 3, cap: 8,[2 3 4]
	//输出 s3: s3 address:0xc000127ee0, data address: 0xc00014dc30, len: 4, cap: 8,[2 3 4 5]

	// s2 添加元素, 不扩容, data address不变(不重新分配内存)
	// s3 对应数据有影响
	s2 = append(s2, 10, 11, 12)
	fmt.Printf("s2: s2 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s2, &s2[0], len(s2), cap(s2), s2)
	fmt.Printf("s3: s3 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s3, &s3[0], len(s3), cap(s3), s3)
	//输出 s2: s2 address:0xc000127f00, data address: 0xc00014dc30, len: 6, cap: 8,[2 3 4 10 11 12]
	//输出 s3: s3 address:0xc000127ee0, data address: 0xc00014dc30, len: 4, cap: 8,[2 3 4 10]


	// s2 添加元素, 扩容, data address变更(重新分配内存)
	// s3 对应数据无影响
	// arr 对应数据无影响
	s2 = append(s2, 10, 11, 12, 13, 14, 15, 16, 17)
	fmt.Printf("arr: arr address:%p, data address: %p, len: %v, cap: %v,%v\n", &arr, &arr[2], len(arr), cap(arr), arr)
	fmt.Printf("s2: s2 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s2, &s2[0], len(s2), cap(s2), s2)
	fmt.Printf("s3: s3 address:%p, data address: %p, len: %v, cap: %v,%v\n", &s3, &s3[0], len(s3), cap(s3), s3)
	//输出 arr: arr address:0xc00014dc20, data address: 0xc00014dc30, len: 10, cap: 10,[0 1 2 3 4 10 11 12 8 9]
	//输出 s2: s2 address:0xc000127f00, data address: 0xc00017d600, len: 14, cap: 16,[2 3 4 10 11 12 10 11 12 13 14 15 16 17]
	//输出 s3: s3 address:0xc000127ee0, data address: 0xc00014dc30, len: 4, cap: 8,[2 3 4 10]

切片扩容大小

如果旧容量小于1024, 则新容量为原来2倍
如果旧容量大于1024, 则新容量按25%的速度增长

切片内存分配

在使用make()分配内存时, 会在底层默认生成一个对应cap大小的数组
如果make的cap小于64KB, 则会在栈内存分配空间
如果make的cap大于64KB, 则会在堆内存分配空间

总结

  1. 从同一个数组索引出来的切片, 底层公用同一份内存数据
  2. 在不引起扩容情况下,任何索引的切片引起的数据变更,也会影响数组及其他索引切片的数据
  3. 在引起扩容情况下,当前操作的切片会重新申请新的内存空间及数据拷贝(内存不大的情况下是原来2倍, 大的情况下是线性增加), 原来数据及切片不受影响

猜你喜欢

转载自blog.csdn.net/ERIC_TWELL/article/details/121253519