append()
💥本文章所有相关go代码参考自go 1.18+版本
- 本篇文章中涉及到汇编,不熟悉请忽略。
使用介绍
- append() 函数用于附加连接切片。
T
是类型S
的元素类型,比如S = []T
。
append(s S, x ...T) S
append
函数将0
个或多个具有相同类型S
的元素追加到切片s
后面并且返回新的切片。- 追加的元素必须和原切片的元素同类型。
- 它的可变参数必须是切片的类型,并返回结果切片,也就是
S
类型。 - 值
x
传递给类型为...
的参数T
,其中T
是S
的元素类型。
- 如果
s
的容量不足以存储新增元素,append
会分配新的切片来保证已有切片元素和新增元素的存储。 - 因此
append()
函数返回的切片可能已经指向一个不同的相关数组了,即使修改了数据也不会同步,具体需要根据S
的容量进行判断。 append()
函数总是返回成功,除非系统内存耗尽了。
|
|
- 切片的元素是空接口。
|
|
- 切片的元素是字节。
|
|
- append() 使用示例。
|
|
|
|
Go中append的描述
- append 内置函数将元素附加到切片的末尾。如果它有足够的容量,目标将被重新切片以容纳新元素。如果没有,将分配一个新的底层数组。
- Append 返回更新后的切片。因此有必要将 append 的结果存储在保存切片本身的变量中:
slice = append(slice, elem1, elem2)
: elem1,elem2 切片元素。slice = append(slice, anotherSlice...)
:anotherSlice 其他切片。
- 作为一种特殊情况,将字符串附加到字节切片是合法的,如下所示:
slice = append([]byte("hello "), "world"...)
- 【
append([]T, ...T) []T
】 或 【append([]byte, string...) []byte
】
|
|
- append() 不扩容时。
|
|
- append() 翻倍扩容时。
|
|
nil切片调用append函数
|
|
TEXT main.main(SB) /mnt/hgfs/g/hello1/slice1.go
func main() {
0x4551e0 493b6610 CMPQ 0x10(R14), SP
0x4551e4 7665 JBE 0x45524b
0x4551e6 4883ec60 SUBQ $0x60, SP
0x4551ea 48896c2458 MOVQ BP, 0x58(SP)
0x4551ef 488d6c2458 LEAQ 0x58(SP), BP
var s []int // {nil, 0, 0}
0x4551f4 48c744244000000000 MOVQ $0x0, 0x40(SP)
0x4551fd 440f117c2448 MOVUPS X15, 0x48(SP)
s = append(s, 1, 2)
0x455203 eb00 JMP 0x455205
0x455205 488d05144b0000 LEAQ 0x4b14(IP), AX # AX=0x4b14(IP) et=&type.int
0x45520c 31db XORL BX, BX # BX=0 old.data
0x45520e 31c9 XORL CX, CX # CX=0 old.len
0x455210 4889cf MOVQ CX, DI # DI=0 old.cap
0x455213 be02000000 MOVL $0x2, SI # SI=2 cap=2
# runtime.growslice -> func growslice(et *_type, old slice, cap int) slice 扩容函数
# 这里就是nil切片能使用append新增元素的原因
0x455218 e8e3b2feff CALL runtime.growslice(SB)
0x45521d 488d5302 LEAQ 0x2(BX), DX
0x455221 eb00 JMP 0x455223
0x455223 48c70001000000 MOVQ $0x1, 0(AX)
0x45522a 48c7400802000000 MOVQ $0x2, 0x8(AX)
0x455232 4889442440 MOVQ AX, 0x40(SP)
0x455237 4889542448 MOVQ DX, 0x48(SP)
0x45523c 48894c2450 MOVQ CX, 0x50(SP)
}
0x455241 488b6c2458 MOVQ 0x58(SP), BP
0x455246 4883c460 ADDQ $0x60, SP
0x45524a c3 RET
func main() {
0x45524b e810cdffff CALL runtime.morestack_noctxt.abi0(SB)
0x455250 eb8e JMP main.main(SB)
// +60 | address of runtime.main
// -----------------------------------
// +58 | BP of runtime.main
// ----------------------------------- BP
// +50 | 0 s.cap
// -----------------------------------
// +48 | 0 s.len
// -----------------------------------
// +40 | 0 s.data
// -----------------------------------
// +38 |
// -----------------------------------
// +30 |
// -----------------------------------
// +28 |
// -----------------------------------
// +20 |
// -----------------------------------
// +18 |
// -----------------------------------
// +10 |
// -----------------------------------
// +08 |
// -----------------------------------
// +00 |
// ----------------------------------- SP
append 执行步骤
- 如果当前
append()
函数执行完后切片不会"翻倍扩容"那么,直接是把append后追加的数据拷贝到切片的后续空间即可。 - 如果当前
append()
函数执行完后需要"翻倍扩容",那么先调用runtime.growslice()
扩容函数,然后在拷贝数据追加到新的内存空间。
append 函数总结
- append() 函数原型
func append(slice []Type, elems ...Type) []Type
支持两种形式。
|
|
|
|
- append() 函数是由编译器支持的没有函数原型只存在函数声明。
func append(slice []Type, elems ...Type) []Type
- 如果添加过程中需要扩容,编译器会调用
runtime.growslice
函数,该函数原型。
|
|