# golang的append原理分析
append是编译器build处理的。
一、看下面的一个简单代码
```
func testappend() {
var buf []byte
buf = append(buf, byte(1))
}
func main() {
testappend()
}
```
对上面的代码golang反汇编
编译指令
go build -gcflags "-S" testbuffer.go
```
"".testappend STEXT size=85 args=0x0 locals=0x20
0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) TEXT "".testappend(SB), $32-0
0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL TLS, CX
0x0007 00007 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL (CX)(TLS*2), CX
0x000d 00013 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CMPL SP, 8(CX)
0x0010 00016 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JLS 78
0x0012 00018 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) SUBL $32, SP
0x0015 00021 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0015 00021 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0015 00021 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) LEAL type.uint8(SB), AX
0x001b 00027 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL AX, (SP)
0x001e 00030 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 4(SP)
0x0026 00038 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 8(SP)
0x002e 00046 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 12(SP)
0x0036 00054 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $1, 16(SP)
0x003e 00062 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) PCDATA $0, $0
0x003e 00062 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) CALL runtime.growslice(SB)
0x0043 00067 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 20(SP), AX
0x0047 00071 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVB $1, (AX)
0x004a 00074 (E:\work\workspace\loggoper\src\test\testbuffer.go:29) ADDL $32, SP
0x004d 00077 (E:\work\workspace\loggoper\src\test\testbuffer.go:29) RET
0x004e 00078 (E:\work\workspace\loggoper\src\test\testbuffer.go:29) NOP
0x004e 00078 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) PCDATA $0, $-1
0x004e 00078 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CALL runtime.morestack_noctxt(SB)
0x0053 00083 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JMP 0
```
所以 append调用的是growslice
关于growslice: go/src/runtime/slice.go
```
// growslice handles slice growth during append.
// It is passed the slice element type, the old slice, and the desired new minimum capacity,
// and it returns a new slice with at least that capacity, with the old data
// copied into it.
// The new slice's length is set to the old slice's length,
// NOT to the new requested capacity.
// This is for codegen convenience. The old slice's length is used immediately
// to calculate where to write new values during an append.
// TODO: When the old backend is gone, reconsider this decision.
// The SSA backend might prefer the new length or to return only ptr/cap and save stack space.
type slice struct {
array unsafe.Pointer
len int
cap int
}
func growslice(et *_type, old slice, cap int) slice {
if raceenabled {
callerpc := getcallerpc(unsafe.Pointer(&et))
racereadrangepc(old.array, uintptr(old.len*int(et.size)), callerpc, funcPC(growslice))
}
if msanenabled {
msanread(old.array, uintptr(old.len*int(et.size)))
}
if et.size == 0 {
if cap < old.cap {
panic(errorString("growslice: cap out of range"))
}
// append should not create a slice with nil pointer but non-zero len.
// We assume that append doesn't need to preserve old.array in this case.
return slice{unsafe.Pointer(&zerobase), old.len, cap}
}
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
if old.len < 1024 {
newcap = doublecap
} else {
for newcap < cap {
newcap += newcap / 4
}
}
}
var lenmem, newlenmem, capmem uintptr
const ptrSize = unsafe.Sizeof((*byte)(nil))
switch et.size {
case 1:
lenmem = uintptr(old.len)
newlenmem = uintptr(cap)
capmem = roundupsize(uintptr(newcap))
newcap = int(capmem)
case ptrSize:
lenmem = uintptr(old.len) * ptrSize
newlenmem = uintptr(cap) * ptrSize
capmem = roundupsize(uintptr(newcap) * ptrSize)
newcap = int(capmem / ptrSize)
default:
lenmem = uintptr(old.len) * et.size
newlenmem = uintptr(cap) * et.size
capmem = roundupsize(uintptr(newcap) * et.size)
newcap = int(capmem / et.size)
}
if cap < old.cap || uintptr(newcap) > maxSliceCap(et.size) {
panic(errorString("growslice: cap out of range"))
}
var p unsafe.Pointer
if et.kind&kindNoPointers != 0 {
p = mallocgc(capmem, nil, false)
memmove(p, old.array, lenmem)
// The append() that calls growslice is going to overwrite from old.len to cap (which will be the new length).
// Only clear the part that will not be overwritten.
memclrNoHeapPointers(add(p, newlenmem), capmem-newlenmem)
} else {
// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan uninitialized memory.
p = mallocgc(capmem, et, true)
if !writeBarrier.enabled {
memmove(p, old.array, lenmem)
} else {
for i := uintptr(0); i < lenmem; i += et.size {
typedmemmove(et, add(p, i), add(old.array, i))
}
}
}
return slice{p, old.len, newcap}
}
```
上面的代码不难看出分配的空间是按照2倍的容量开辟的.
我们把上面的测试代码稍微改一下,改成如下格式:
```
func testappend() {
var buf []byte
buf = append(buf, byte(1))
buf = append(buf, byte(2))
buf = buf[:0]
buf = append(buf, byte(3))
}
```
最后的一行buf = append(buf, byte(3)) 会在重新分配吗?
```
"".testappend STEXT size=215 args=0x0 locals=0x24
0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) TEXT "".testappend(SB), $36-0
0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL TLS, CX
0x0007 00007 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL (CX)(TLS*2), CX
0x000d 00013 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CMPL SP, 8(CX)
0x0010 00016 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JLS 205
0x0016 00022 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) SUBL $36, SP
0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) LEAL type.uint8(SB), AX
0x001f 00031 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL AX, (SP)
0x0022 00034 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 4(SP)
0x002a 00042 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 8(SP)
0x0032 00050 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 12(SP)
0x003a 00058 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $1, 16(SP)
0x0042 00066 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) PCDATA $0, $0
0x0042 00066 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) CALL runtime.growslice(SB)
0x0047 00071 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 20(SP), AX
0x004b 00075 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 24(SP), CX
0x004f 00079 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 28(SP), DX
0x0053 00083 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVB $1, (AX)
0x0056 00086 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) LEAL 1(CX), BX
0x0059 00089 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) LEAL 2(CX), BP
0x005c 00092 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) CMPL BP, DX
0x005e 00094 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) JGT 157
0x0060 00096 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVB $2, 1(CX)(AX*1)
0x0065 00101 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) CMPL DX, $1
0x0068 00104 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) JLT 113
0x006a 00106 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVB $3, (AX)
0x006d 00109 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) ADDL $36, SP
0x0070 00112 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) RET
0x0071 00113 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) LEAL type.uint8(SB), CX
0x0077 00119 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL CX, (SP)
0x007a 00122 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL AX, 4(SP)
0x007e 00126 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL $0, 8(SP)
0x0086 00134 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL DX, 12(SP)
0x008a 00138 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL $1, 16(SP)
0x0092 00146 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) PCDATA $0, $0
0x0092 00146 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) CALL runtime.growslice(SB)
0x0097 00151 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL 20(SP), AX
0x009b 00155 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) JMP 106
0x009d 00157 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL CX, ""..autotmp_4+32(SP)
0x00a1 00161 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) LEAL type.uint8(SB), CX
0x00a7 00167 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL CX, (SP)
0x00aa 00170 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL AX, 4(SP)
0x00ae 00174 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL BX, 8(SP)
0x00b2 00178 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL DX, 12(SP)
0x00b6 00182 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL BP, 16(SP)
0x00ba 00186 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) PCDATA $0, $0
0x00ba 00186 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) CALL runtime.growslice(SB)
0x00bf 00191 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 20(SP), AX
0x00c3 00195 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 28(SP), DX
0x00c7 00199 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL ""..autotmp_4+32(SP), CX
0x00cb 00203 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) JMP 96
0x00cd 00205 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) NOP
0x00cd 00205 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) PCDATA $0, $-1
0x00cd 00205 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CALL runtime.morestack_noctxt(SB)
0x00d2 00210 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JMP 0
```
![image-20210221160812103](C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20210221160812103.png)
如果不执行30行的代码 再反编译看看。
```
"".testappend STEXT size=225 args=0x0 locals=0x28
0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) TEXT "".testappend(SB), $40-0
0x0000 00000 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL TLS, CX
0x0007 00007 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) MOVL (CX)(TLS*2), CX
0x000d 00013 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CMPL SP, 8(CX)
0x0010 00016 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JLS 215
0x0016 00022 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) SUBL $40, SP
0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0019 00025 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) LEAL type.uint8(SB), AX
0x001f 00031 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL AX, (SP)
0x0022 00034 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 4(SP)
0x002a 00042 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 8(SP)
0x0032 00050 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $0, 12(SP)
0x003a 00058 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL $1, 16(SP)
0x0042 00066 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) PCDATA $0, $0
0x0042 00066 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) CALL runtime.growslice(SB)
0x0047 00071 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 20(SP), AX
0x004b 00075 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 24(SP), CX
0x004f 00079 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVL 28(SP), DX
0x0053 00083 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) MOVB $1, (AX)
0x0056 00086 (E:\work\workspace\loggoper\src\test\testbuffer.go:27) LEAL 1(CX), BX
0x0059 00089 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) LEAL 2(CX), BP
0x005c 00092 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) CMPL BP, DX
0x005e 00094 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) JGT 160
0x0060 00096 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVB $2, 1(CX)(AX*1)
0x0065 00101 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) LEAL 1(BP), CX
0x0068 00104 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) CMPL CX, DX
0x006a 00106 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) JGT 116
0x006c 00108 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVB $3, (AX)(BP*1)
0x0070 00112 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) ADDL $40, SP
0x0073 00115 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) RET
0x0074 00116 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) MOVL BP, "".buf.len+32(SP)
0x0078 00120 (E:\work\workspace\loggoper\src\test\testbuffer.go:33) LEAL type.uint8(SB), BX
0x007e 00126 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL BX, (SP)
0x0081 00129 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL AX, 4(SP)
0x0085 00133 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL BP, 8(SP)
0x0089 00137 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL DX, 12(SP)
0x008d 00141 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL CX, 16(SP)
0x0091 00145 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) PCDATA $0, $0
0x0091 00145 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) CALL runtime.growslice(SB)
0x0096 00150 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL 20(SP), AX
0x009a 00154 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL "".buf.len+32(SP), BP
0x009e 00158 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) JMP 108
0x00a0 00160 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) MOVL CX, ""..autotmp_4+36(SP)
0x00a4 00164 (E:\work\workspace\loggoper\src\test\testbuffer.go:31) LEAL type.uint8(SB), CX
0x00aa 00170 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL CX, (SP)
0x00ad 00173 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL AX, 4(SP)
0x00b1 00177 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL BX, 8(SP)
0x00b5 00181 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL DX, 12(SP)
0x00b9 00185 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL BP, 16(SP)
0x00bd 00189 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) PCDATA $0, $0
0x00bd 00189 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) CALL runtime.growslice(SB)
0x00c2 00194 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 20(SP), AX
0x00c6 00198 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 24(SP), CX
0x00ca 00202 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL 28(SP), DX
0x00ce 00206 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) LEAL 1(CX), BP
0x00d1 00209 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) MOVL ""..autotmp_4+36(SP), CX
0x00d5 00213 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) JMP 96
0x00d7 00215 (E:\work\workspace\loggoper\src\test\testbuffer.go:28) NOP
0x00d7 00215 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) PCDATA $0, $-1
0x00d7 00215 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) CALL runtime.morestack_noctxt(SB)
0x00dc 00220 (E:\work\workspace\loggoper\src\test\testbuffer.go:25) JMP 0
```
总结:通过汇编代码不难看出 ,即使len为0了,还是会从原来的分配中赋值。
有疑问加站长微信联系(非本文作者))