# 10分钟带你搞定slice底层原理！

### 底层结构

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

slice一共有3个字段。其中，array代表底层数组的指针，len 代表 slice的长度，cap代表slice的长度，也是底层数组的长度。

### 扩容机制

``````newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
const threshold = 256
if old.cap < threshold {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
// 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 = cap
}
}
}

var overflow bool
var lenmem, newlenmem, capmem uintptr
// Specialize for common values of et.size.
// For 1 we don't need any division/multiplication.
// For goarch.PtrSize, compiler will optimize division/multiplication into a shift by a constant.
// For powers of 2, use a variable shift.
switch {
case et.size == 1:
lenmem = uintptr(old.len)
newlenmem = uintptr(cap)
capmem = roundupsize(uintptr(newcap))
overflow = uintptr(newcap) > maxAlloc
newcap = int(capmem)
case et.size == goarch.PtrSize:
lenmem = uintptr(old.len) * goarch.PtrSize
newlenmem = uintptr(cap) * goarch.PtrSize
capmem = roundupsize(uintptr(newcap) * goarch.PtrSize)
overflow = uintptr(newcap) > maxAlloc/goarch.PtrSize
newcap = int(capmem / goarch.PtrSize)
case isPowerOfTwo(et.size):
var shift uintptr
if goarch.PtrSize == 8 {
// Mask shift for better code generation.
shift = uintptr(sys.Ctz64(uint64(et.size))) & 63
} else {
shift = uintptr(sys.Ctz32(uint32(et.size))) & 31
}
lenmem = uintptr(old.len) << shift
newlenmem = uintptr(cap) << shift
capmem = roundupsize(uintptr(newcap) << shift)
overflow = uintptr(newcap) > (maxAlloc >> shift)
newcap = int(capmem >> shift)
default:
lenmem = uintptr(old.len) * et.size
newlenmem = uintptr(cap) * et.size
capmem, overflow = math.MulUintptr(et.size, uintptr(newcap))
capmem = roundupsize(capmem)
newcap = int(capmem / et.size)
}``````

• 如果新申请长度大于 2 倍的旧容量，那么最终容量就是新的切片长度 。
• 如果旧容量小于256，那么最终容量就是旧容量的两倍 。
• 如果旧容量大于256，最终容量不一定是多少，因为涉及到移位。（大概是之前容量的1.25倍）
• 如果最终容量计算值溢出，则最终容量就是新的切片长度。

### 思考题

``````func main() {
a := []int{1,2,3}
slice(a)
fmt.Println("1", a)

slicePtr1(&a)2
fmt.Println("2", a)

slicePtr2(&a)
fmt.Println("3", a)

slicePtr3(&a)
}

func slice(s []int) {
s[0] = 10
s = append(s, 10)
s[1] = 10
}

func slicePtr1(s *[]int) {
(*s)[0] = 20
*s = append(*s, 20)
(*s)[1] = 20
}

func slicePtr2(s *[]int) {
b := *s
b[0] = 30
b = append(b, 30)
b[1] = 30
}

func slicePtr3(s *[]int) {
b := *s
b = append(b, 40)
fmt.Println("4", b)
*s = append(*s, 50)
fmt.Println("5", b)
}
//请问最后会打印出什么？``````

### 最后

www.yuque.com/shier-zone

(=￣ω￣=)··· 暂无内容！

1

0

1

2