Golang将一个项目附加到切片上

Pol*_*ang 65 go

为什么切片a保持不变?是否append()产生一个新的片?

package main

import (
    "fmt"
)

var a = make([]int, 7, 8)

func Test(slice []int) {
    slice = append(slice, 100)
    fmt.Println(slice)
}

func main() {
    for i := 0; i < 7; i++ {
        a[i] = i
    }

    Test(a)
    fmt.Println(a)
}
Run Code Online (Sandbox Code Playgroud)

输出:

[0 1 2 3 4 5 6 100]
[0 1 2 3 4 5 6]
Run Code Online (Sandbox Code Playgroud)

kos*_*tix 51

在您的示例slice中,Test函数的参数在调用者的范围内接收变量的副本a.

由于切片变量包含仅仅引用基础数组的"切片描述符" ,因此在您的Test函数中,您可以slice连续多次修改变量中保存的切片描述符,但这不会影响调用者及其a变量.

Test函数内部,第一个append重新分配slice变量下的后备数组,复制其原始内容,附加100到它,这就是你正在观察的内容.退出时Test,slice变量超出范围,切片引用的(新)基础数组也超出范围.

如果你想Test表现得像append,你必须从它返回新的切片 - 就像append那样 - 并要求调用者Test以他们使用的相同方式使用它append:

func Test(slice []int) []int {
    slice = append(slice, 100)

    fmt.Println(slice)

    return slice
}

a = Test(a)
Run Code Online (Sandbox Code Playgroud)

请仔细阅读本文,因为它基本上向您展示了如何append在解释切片如何在内部工作之后手动实现.然后读这个.

  • 我实际上认为这种描述是微妙的.下面@doun的答案实际上是对内部发生的更正确的表示:`Test`中的`append`不会重新分配任何内存,因为数组后备片`a`的原始分配仍然可以适合单个附加项.换句话说,在编写这个程序时,`Test(a)`和`a`的返回值是不同长度的不同切片头,但它们指向完全相同的底层数组.打印`fmt.Println(a [:cap(a)]`作为`main`函数的最后一行使这个清楚. (5认同)

Fre*_*Foo 27

典型append用法是

a = append(a, x)
Run Code Online (Sandbox Code Playgroud)

因为append可以就地修改其参数或者使用附加条目返回其参数的副本,具体取决于其输入的大小和容量.使用先前附加的切片可能会产生意外结果,例如

a := []int{1,2,3}
a = append(a, 4)
fmt.Println(a)
append(a[:3], 5)
fmt.Println(a)
Run Code Online (Sandbox Code Playgroud)

可以打印

[1 2 3 4]
[1 2 3 5]
Run Code Online (Sandbox Code Playgroud)

  • 在Go操场上试过.代码不起作用. (3认同)
  • 这需要是 `_ =append(a[:3], 5)` 才能立即编译 (2认同)

Dav*_*phy 11

为了使您的代码工作而不必从 Test 返回切片,您可以传递这样的指针:

package main

import (
    "fmt"
)

var a = make([]int, 7, 8)

func Test(slice *[]int) {
    *slice = append(*slice, 100)

    fmt.Println(*slice)
}

func main() {

    for i := 0; i < 7; i++ {
        a[i] = i
    }

    Test(&a)

    fmt.Println(a)
}
Run Code Online (Sandbox Code Playgroud)


Giz*_*zak 5

注意如果上限不足,追加将生成新切片.@ kostix的答案是正确的,或者你可以通过指针传递切片参数!

  • @kostix你是对的,代码的目的应该是明确的。但我认为整个故事只是传递一个存储指针的值和传递一个指向指针的指针。如果我们修改引用,两者都可以工作,但是如果我们替换引用,第一个就会失去效果。程序员应该知道他在做什么。 (2认同)

dou*_*oun 5

试试这个,我认为这一点很清楚.底层数组被更改,但我们的切片不是,print只是打印len()字符,通过另一个切片到cap(),你可以看到更改的数组:

func main() {

  for i := 0; i < 7; i++ {
      a[i] = i
  }

  Test(a)

  fmt.Println(a) // prints [0..6]
  fmt.Println(a[:cap(a)] // prints [0..6,100]
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,如果你运行代码,你会发现它.因为在测试(a)调用期间更改了cap(a) (2认同)