​深入了解Go Slice(三)—— append的处理过程

前言

深入了解Go Slice(一)—— make的详细处理过程

深入了解Go Slice(二)—— 切片的详细处理过程

这两篇文章分别介绍了从make、array/slice切片构造slice的具体底层处理过程,本文则介绍通过append生成新的slice的过程。

Slice append

append func

// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//  slice = append(slice, elem1, elem2)
//  slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
//  slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type

以上为builtin/builtin.go中关于append func的说明。append会返回一个新的slice,因此必须保存append的结果。

我们知道,append会追加一个或多个数据至slice中,这些数据会存储在slice的持有的数组中。
数组的长度是固定的,意味着存储的数据是有限的。剩余空间足以容纳追加的数据,则可以正常将数据存入数组。一旦追加数据后总长度超过数组长度后,则原数组已无法存储新数据。那要怎么处理呢?

runtime/slice.go中只有扩容的growslice func,其调用主要在cmd/compile/internal/gc/walk.go中,处理相对复杂。我们可以先看下refelct/value.go下的Append源码,此部分的处理过程很完整和简单。

reflect Append

// Append appends the values x to a slice s and returns the resulting slice.
// As in Go, each x's value must be assignable to the slice's element type.
func Append(s Value, x ...Value) Value {
   
   
    s.mustBe(Slice)
    s, i0, i1 := grow(s, len(x))
    for i, j := i0, 0; i < i1; i, j = i+1, j+1 {
   
   
        s.Index(i).Set(x[j])
    }
    return s
}
// grow grows the slice s so that it can hold extra more values, allocating
// more capacity if needed. It also returns the old and new slice lengths.
func grow(s Value, extra int) (Value, int, int) {
   
   
    i0 := s.Len()
    i1 := i0 + extra
    if i1 < i0 {
   
   
        panic("reflect.Append: slice overflow")
    }
    m := s.Cap()
    if i1 <= m {
   
   
        return s.Slice(0, i1), i0, i1
    }
    if m == 0 {
   
   
        m = extra
    } else {
   
   
        for m < i1 {
   
   
            if i0 < 1024 {
   
   
                m += m
            } else {
   
   
                m += m / 4
            }
        }
    }
    t := MakeSlice(s.Type(), i1, m)
    Copy(t, s)
    return t, i0, i1
}

Append处理过程如下:

  1. 判断当前slice长度i0与追加数据的总长度i1是否溢出,溢出则报错;
  2. 若i1小于/等于slice的cap(底层数组的长度),直接返回原slice的起始及结束数据部分
  3. 否则,当前底层数组已无法存储所有的追加数据,需要进行扩容处理:
  • 若当前cap为0,则直接已追加数据的长度为新cap;

  • 若i1大于slice的cap m,开始逐步扩容cap,直至大于总数据总长i1

    • 若原数据长度i0<1024,则m翻倍;
    • 否则,m自增1/4
  • 构建新的Slice

  • 将原slice的数据拷贝至新slice中,并返回新slice。

  1. 将追加的数据存入指定的位置中

append

append的具体调用处理在cmd/compile/internal/gc/walk.go中,核心处理代码如下:

// Node ops.
const (
    OXXX Op = iota
    ...
    OAPPEND       // append(List); after walk, Left may contain elem type descriptor
    ...
)
...
...
case OAPPEND:
    // x = append(...)
    r := n.Right
    if r.Type.Elem().NotInHeap() {
   
   
    yyerror("%v is go:notinheap; heap allocationdisallowed"
04-16
### 使用 `append` 的编程方法 在多种编程语言中,`append` 是一种常见的操作,用于向数据结构(如列表、数组或其他集合)追加新元素。以下是几种常见编程语言中的具体实现方式。 #### Python 中的 Append 方法 Python 提供了一个简单的方法来扩展列表的内容——通过调用 `.append()` 函数。此函数会将单个对象作为参数并将其添加到列表的末尾[^3]。 ```python my_list = [1, 2, 3] my_list.append(4) # 将整数 4 添加到 my_list 列表的最后位置 print(my_list) # 输出: [1, 2, 3, 4] ``` 如果需要一次性添加多个元素,则可以考虑使用 `.extend()` 方法而不是多次调用 `.append()`. ```python my_list.extend([5, 6]) # 向 my_list 追加两个新的数值 print(my_list) # 输出: [1, 2, 3, 4, 5, 6] ``` #### Go 语言中的 Append 功能 Go 编程语言也支持类似的机制,不过它采用的是内置函数形式而非成员方法。该函数名为 `append` 并接受切片(slice)以及要附加的一个或者更多项作为输入参数[^2]。 ```go package main import "fmt" func main() { var numbers []int numbers = append(numbers, 1) // 单独增加一项 fmt.Println(numbers) // 打印当前状态下的numbers变量 additional := []int{2, 3, 4} numbers = append(numbers, additional...) // 可以一次加入整个slice里的所有项目 fmt.Println(numbers) // 显示最终结果为:[1 2 3 4] } ``` 注意这里的语法细节:当希望把另一个 slice 整体拼接到目标 slice 上时,在源 slice 名字后面加上省略号(`...`)表示展开其内部各个独立成分逐一参与连接过程[^2]. #### MySQL 数据库环境设置与管理命令无关于此话题讨论范围之内因此不做赘述[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值