数组的初始化

  1. package main
  2. import "fmt"
  3. func echo(x [4]int) {
  4. fmt.Println(x)
  5. }
  6. func main() {
  7. // 变量 a 类型为 [4]int 是一个 type,每个元素自动初始化为 int 的零值(zero-value)
  8. var a [4]int
  9. // 变量 b 类型为 [5]int 是不同于 [4]int 的类型,且 b[4] 会自动初始化为 int 的零值
  10. b := [5]int{1, 2, 3, 4}
  11. // 变量 c 被自动推导为 [5]int 类型,与 b 类型同
  12. c := [...]int{1, 2, 3, 4, 5}
  13. // a里面的所有元素都会被复制一遍、因为go语言的函数传参是 值传递
  14. echo(a)
  15. fmt.Println("b: ", b)
  16. fmt.Println("c: ", c)
  17. }

slice 初始化

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 借助 make 函数,此时 len = cap = 5,每个元素初始化为 byte 的 zero-value
  5. s0 := make([]byte, 5)
  6. // 字面值初始化,此时 len = cap = 5
  7. s1 := []byte{0, 0, 0, 0, 0}
  8. // 自动初始化为 slice 的“零值(zero-value)”:nil
  9. var s2 []byte
  10. fmt.Println("s0: ", s0)
  11. fmt.Println("s1: ", s1)
  12. fmt.Println("s2: ", s2)
  13. }

数组和切片的不同

  • 数组定义后长度不可变、而切片可变
  • 切片是引用类型、而数组是 值类型、
  • 引用类型的除了切片还有map、channel、函数等
  • 值类型的有 基础数据类型 和 结构体类型

查看数组及切片的长度、容量

  1. package main
  2. import "fmt"
  3. func main() {
  4. // 创建一个slice 其实就是分配内存、cap 和 len的设置是在汇编中完成的
  5. s1 := make([]int, 5)
  6. fmt.Printf("The length of s1: %d\n", len(s1))
  7. fmt.Printf("The capacity of s1: %d\n", cap(s1))
  8. fmt.Printf("The value of s1: %d\n", s1)
  9. s2 := make([]int, 5, 8)
  10. fmt.Printf("The length of s2: %d\n", len(s2))
  11. fmt.Printf("The capacity of s2: %d\n", cap(s2))
  12. fmt.Printf("The value of s2: %d\n", s2)
  13. }

切片同时 指向一个底层数组

  1. package main
  2. import "fmt"
  3. func main() {
  4. s3 := []int{1, 2, 3, 4, 5, 6, 7, 8}
  5. s4 := s3[3:6]
  6. fmt.Printf("The length of s4: %d\n", len(s4))
  7. fmt.Printf("The capacity of s4: %d\n", cap(s4))
  8. fmt.Printf("The value of s4: %d\n", s4)
  9. fmt.Printf("s3中的 元素4地址是: %d\n", &s3[3])
  10. fmt.Printf("s4中的 元素4地址是: %d\n", &s4[0])
  11. }
  12. /**
  13. Output:
  14. The length of s4: 3
  15. The capacity of s4: 5
  16. The value of s4: [4 5 6]
  17. s3中的 元素4地址是: 824633827544
  18. s4中的 元素4地址是: 824633827544
  19. */

切片的底层数组 什么时候被替换?

  • 准确来说 一个切片的底层数组永远不会被替换
  • 虽然扩容时、Go语言一定会生成一个新的底层数组、但同时也生成了新的切片
  • 在 容量足够的情况下、append函数返回的是 指向原切片的底层数组
  • append 需要扩容时、返回的是 指向 新底层数组的新切片

slice 源码解析

代码位置 runtime/slice.go

make

  1. func makeslice(et *_type, len, cap int) unsafe.Pointer {
  2. // 获取需要申请的内存大小
  3. // overflow 乘法是否溢出、这里指的是 size * cap 是否溢出
  4. // mem 等于 size * cap
  5. mem, overflow := math.MulUintptr(et.size, uintptr(cap))
  6. // 如果溢出了 或
  7. // size * cap大于最大分配 或
  8. // len 小于0 或
  9. // len 大于 cap
  10. // 都会报错、len上限超出范围 或者 cap上限超出范围
  11. if overflow || mem > maxAlloc || len < 0 || len > cap {
  12. // NOTE: Produce a 'len out of range' error instead of a
  13. // 'cap out of range' error when someone does make([]T, bignumber).
  14. // 'cap out of range' is true too, but since the cap is only being
  15. // supplied implicitly, saying len is clearer.
  16. // See golang.org/issue/4085.
  17. mem, overflow := math.MulUintptr(et.size, uintptr(len))
  18. if overflow || mem > maxAlloc || len < 0 {
  19. panicmakeslicelen()
  20. }
  21. panicmakeslicecap()
  22. }
  23. // 分配 slice内存
  24. // 小对象从 当前 P的 cache中空闲数据里面分配
  25. // 大对象 (size > 32KB) 直接从 heap(堆) 中分配
  26. return mallocgc(mem, et, true)
  27. }

append

  1. func growslice(et *_type, old slice, cap int) slice {
  2. // 是否开启 race检测、也就是数据竞争检测
  3. if raceenabled {
  4. callerpc := getcallerpc()
  5. racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice))
  6. }
  7. // 是否开启内存扫描
  8. if msanenabled {
  9. msanread(old.array, uintptr(old.len*int(et.size)))
  10. }
  11. // 新容量如果小于 当前容量直接 panic
  12. if cap < old.cap {
  13. panic(errorString("growslice: cap out of range"))
  14. }
  15. // 存储的类型空间为0、长度不为空、则重新创建
  16. if et.size == 0 {
  17. // append should not create a slice with nil pointer but non-zero len.
  18. // We assume that append doesn't need to preserve old.array in this case.
  19. return slice{unsafe.Pointer(&zerobase), old.len, cap}
  20. }
  21. newcap := old.cap
  22. doublecap := newcap + newcap
  23. if cap > doublecap {
  24. // 如果新容量大于 原有容量的两倍、则直接按照新增容量大小申请
  25. newcap = cap
  26. } else {
  27. if old.len < 1024 {
  28. // 如果原有长度 小于1024 kb、新容量则按照两倍创建
  29. newcap = doublecap
  30. } else {
  31. // 大于1024kb 按照原有容量的 1/4 扩容、直到满足新容量的需要
  32. // Check 0 < newcap to detect overflow
  33. // and prevent an infinite loop.
  34. for 0 < newcap && newcap < cap {
  35. newcap += newcap / 4
  36. }
  37. // 检查新容量 是否溢出、如果溢出 则使用现有的容量
  38. // Set newcap to the requested cap when
  39. // the newcap calculation overflowed.
  40. if newcap <= 0 {
  41. newcap = cap
  42. }
  43. }
  44. }
  45. // 为了加速计算、对于不同的slice 元素大小、
  46. // 选择不同的计算方法、获取需要申请的内存大小
  47. var overflow bool
  48. var lenmem, newlenmem, capmem uintptr
  49. // Specialize for common values of et.size.
  50. // For 1 we don't need any division/multiplication.
  51. // For sys.PtrSize, compiler will optimize division/multiplication into a shift by a constant.
  52. // For powers of 2, use a variable shift.
  53. switch {
  54. case et.size == 1:
  55. lenmem = uintptr(old.len)
  56. newlenmem = uintptr(cap)
  57. capmem = roundupsize(uintptr(newcap))
  58. overflow = uintptr(newcap) > maxAlloc
  59. newcap = int(capmem)
  60. case et.size == sys.PtrSize:
  61. lenmem = uintptr(old.len) * sys.PtrSize
  62. newlenmem = uintptr(cap) * sys.PtrSize
  63. capmem = roundupsize(uintptr(newcap) * sys.PtrSize)
  64. overflow = uintptr(newcap) > maxAlloc/sys.PtrSize
  65. newcap = int(capmem / sys.PtrSize)
  66. case isPowerOfTwo(et.size):
  67. // 2的倍数 用位移计算
  68. var shift uintptr
  69. if sys.PtrSize == 8 {
  70. // Mask shift for better code generation.
  71. shift = uintptr(sys.Ctz64(uint64(et.size))) & 63
  72. } else {
  73. shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
  74. }
  75. lenmem = uintptr(old.len) << shift
  76. newlenmem = uintptr(cap) << shift
  77. capmem = roundupsize(uintptr(newcap) << shift)
  78. overflow = uintptr(newcap) > (maxAlloc >> shift)
  79. newcap = int(capmem >> shift)
  80. default:
  81. // 其他计算用 除法
  82. lenmem = uintptr(old.len) * et.size
  83. newlenmem = uintptr(cap) * et.size
  84. capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
  85. capmem = roundupsize(capmem)
  86. newcap = int(capmem / et.size)
  87. }
  88. // The check of overflow in addition to capmem > maxAlloc is needed
  89. // to prevent an overflow which can be used to trigger a segfault
  90. // on 32bit architectures with this example program:
  91. //
  92. // type T [1<<27 + 1]int64
  93. //
  94. // var d T
  95. // var s []T
  96. //
  97. // func main() {
  98. // s = append(s, d, d, d, d)
  99. // print(len(s), "\n")
  100. // }
  101. // 判断是否溢出
  102. if overflow || capmem > maxAlloc {
  103. panic(errorString("growslice: cap out of range"))
  104. }
  105. // 内存分配
  106. var p unsafe.Pointer
  107. if et.ptrdata == 0 {
  108. p = mallocgc(capmem, nil, false)
  109. // The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length).
  110. // Only clear the part that will not be overwritten.
  111. // 清空不需要数据拷贝的部分内存
  112. memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
  113. } else {
  114. // Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
  115. p = mallocgc(capmem, et, true)
  116. // gc相关
  117. if lenmem > 0 && writeBarrier.enabled {
  118. // Only shade the pointers in old.array since we know the destination slice p
  119. // only contains nil pointers because it has been cleared during alloc.
  120. bulkBarrierPreWriteSrcOnly(uintptr(p), uintptr(old.array), lenmem)
  121. }
  122. }
  123. // 数据拷贝
  124. memmove(p, old.array, lenmem)
  125. return slice{p, old.len, newcap}
  126. }

copy

  1. func slicecopy(to, fm slice, width uintptr) int {
  2. if fm.len == 0 || to.len == 0 {
  3. return 0
  4. }
  5. n := fm.len
  6. if to.len < n {
  7. n = to.len
  8. }
  9. // 元素大小为0、直接返回
  10. if width == 0 {
  11. return n
  12. }
  13. // 数据竞争检测
  14. if raceenabled {
  15. callerpc := getcallerpc()
  16. pc := funcPC(slicecopy)
  17. racewriterangepc(to.array, uintptr(n*int(width)), callerpc, pc)
  18. racereadrangepc(fm.array, uintptr(n*int(width)), callerpc, pc)
  19. }
  20. // 内存扫描
  21. if msanenabled {
  22. msanwrite(to.array, uintptr(n*int(width)))
  23. msanread(fm.array, uintptr(n*int(width)))
  24. }
  25. size := uintptr(n) * width
  26. if size == 1 { // common case worth about 2x to do here
  27. // TODO: is this still worth it with new memmove impl?
  28. // 直接拷贝
  29. *(*byte)(to.array) = *(*byte)(fm.array) // known to be a byte pointer
  30. } else {
  31. // 拷贝
  32. memmove(to.array, fm.array, size)
  33. }
  34. return n
  35. }
  36. // 字符串slice的拷贝
  37. func slicestringcopy(to []byte, fm string) int {
  38. if len(fm) == 0 || len(to) == 0 {
  39. return 0
  40. }
  41. n := len(fm)
  42. if len(to) < n {
  43. n = len(to)
  44. }
  45. if raceenabled {
  46. callerpc := getcallerpc()
  47. pc := funcPC(slicestringcopy)
  48. racewriterangepc(unsafe.Pointer(&to[0]), uintptr(n), callerpc, pc)
  49. }
  50. if msanenabled {
  51. msanwrite(unsafe.Pointer(&to[0]), uintptr(n))
  52. }
  53. memmove(unsafe.Pointer(&to[0]), stringStructOf(&fm).str, uintptr(n))
  54. return n
  55. }