append to a slice in go
Table of Contents
本文操作 go 版本为 1.15.1
1. 一段非常简单的代码
上面这段代码输出什么呢?
如果你能正确的说出输出了什么以及为什么,这篇博客也没有必要继续阅读了
2. 在 append 之前先来看看 slice 本来面目
array 是底层数组的指针
len 是 slice 长度
cap 是 slice 容量
3. append 究竟做了什么
先看看 append 做了什么事情
append 的实现在 ssa 中
r := s.rtcall(growslice, true, []*types.Type{pt, types.Types[TINT], types.Types[TINT]}, taddr, p, l, c, nl)
在这里通过 rtcall 来调用 slice 中的 growslice 函数
func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args ...*ssa.Value) []*ssa.Value {}
所以分为两种情况,需要增长和不需要增长
大概流程如下
// If inplace is false, process as expression "append(s, e1, e2, e3)": // // ptr, len, cap := s // newlen := len + 3 // if newlen > cap { // ptr, len, cap = growslice(s, newlen) // newlen = len + 3 // recalculate to avoid a spill // } // // with write barriers, if needed: // *(ptr+len) = e1 // *(ptr+len+1) = e2 // *(ptr+len+2) = e3 // return makeslice(ptr, newlen, cap) // // // If inplace is true, process as statement "s = append(s, e1, e2, e3)": // // a := &s // ptr, len, cap := s // newlen := len + 3 // if uint(newlen) > uint(cap) { // newptr, len, newcap = growslice(ptr, len, cap, newlen) // vardef(a) // if necessary, advise liveness we are writing a new a // *a.cap = newcap // write before ptr to avoid a spill // *a.ptr = newptr // with write barrier // } // newlen = len + 3 // recalculate to avoid a spill // *a.len = newlen // // with write barriers, if needed: // *(ptr+len) = e1 // *(ptr+len+1) = e2 // *(ptr+len+2) = e3
在调用 append 时,直接对底层数组的下标进行修改,这就可以解释在最开始的代码的现象
4. 一句话讲完系列
append 有点坑,并不是真的向尾添加,而是根据当前的指针移动位置,改写底层数组的下标