append to a slice in go

Table of Contents

本文操作 go 版本为 1.15.1

1. 一段非常简单的代码

50586599808_4cbf6ba699_h.jpg 上面这段代码输出什么呢?

50587343766_ac6a85c7ca_h.jpg

如果你能正确的说出输出了什么以及为什么,这篇博客也没有必要继续阅读了

2. 在 append 之前先来看看 slice 本来面目

50586599823_6fab39125a.jpg

array 是底层数组的指针

len 是 slice 长度

cap 是 slice 容量

50587464757_648e3d8086_h.jpg

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 有点坑,并不是真的向尾添加,而是根据当前的指针移动位置,改写底层数组的下标

Created: 2023-11-24 Fri 09:50

Validate