将结构属性(切片)传递给从中删除元素的函数时的奇怪行为

这些天我开始学习 Go 并陷入尝试将结构属性的值(切片)传递给函数的过程中。显然它是作为引用传递的(或者它持有指向其切片的指针),并且在函数内部所做的更改会影响它。


这是我的代码,它testFunction应该接收一个切片,删除它的前 3 个元素并打印更新的值,但不会在外部影响它:


package main


import (

    "fmt"

)


type testStruct struct {

    testArray []float64

}


var test = testStruct {

    testArray: []float64{10,20,30,40,50},

}


func main() {

    fmt.Println(test.testArray)

    testFunction(test.testArray)

    fmt.Println(test.testArray)

}


func testFunction(array []float64) {

    for i:=0; i<3; i++ {

        array = removeFrom(array, 0)

    }

    fmt.Println(array)

}


func removeFrom(array []float64, index int) []float64 {

    return append(array[:index], array[index+1:]...)

}

输出:


[10 20 30 40 50]

[40 50]

[40 50 50 50 50]

我的问题是:是什么导致第三个fmt.Println打印出这个奇怪的结果?


游乐场:https : //play.golang.org/p/G8W3H085In


ps:这段代码只是一个例子。删除某些东西的第一个元素不是我的目标。我只想知道是什么导致了这种奇怪的行为。


哈士奇WWW
浏览 139回答 3
3回答

慕莱坞森

通常我们不知道给定的 append 调用是否会导致重新分配,因此我们不能假设原始切片与结果切片引用相同的数组,也不能假设它引用不同的数组。要正确使用切片,重要的是要记住虽然底层数组的元素是间接的,但切片的指针、长度和容量却不是。因此,通常将调用的结果分配给 append 到同一个切片变量:array = append(array, ...)总而言之,要获得所需的结果,请始终记住将 append 函数分配给新的或相同的切片变量。这是更正且有效的代码:package mainimport (&nbsp; &nbsp; "fmt")type testStruct struct {&nbsp; &nbsp; testArray []float64}var test = testStruct {&nbsp; &nbsp; testArray: []float64{10,20,30,40,50},}func main() {&nbsp; &nbsp; fmt.Println(test.testArray)&nbsp; &nbsp; a := testFunction(test.testArray)&nbsp; &nbsp; fmt.Println(a)}func testFunction(array []float64)[]float64 {&nbsp; &nbsp; for i:=0; i<3; i++ {&nbsp; &nbsp; &nbsp; &nbsp; array = removeFrom(array, 0)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println(array)&nbsp; &nbsp; return array}func removeFrom(array []float64, index int) []float64 {&nbsp; &nbsp; return append(array[:index], array[index+1:]...)}检查它的工作代码Go Playground。另一种解决方案是通过指针引用传递数组参数:func testFunction(array *[]float64) {&nbsp; &nbsp; for i:=0; i<3; i++ {&nbsp; &nbsp; &nbsp; &nbsp; *array = removeFrom(*array, 0)&nbsp; &nbsp; }&nbsp; &nbsp; fmt.Println(*array)}

犯罪嫌疑人X

切片是复合类型。它有一个指向数据、长度和容量的指针。当您将它作为参数传递时,您将传递这些值、指针、长度和容量;它们总是副本。在您的情况下,您在调用时修改切片中的数据removeFrom(),您可以这样做,因为您已将指向原始数据的指针的值复制到 func 中,但长度和容量在该函数范围之外保持不变那些不是指针。因此,当您再次打印它时,main()您会看到更改后的值,但它仍然使用原始长度和容量,因为对其他函数范围内的那些所做的任何更改实际上都是在这些值的副本上。

呼唤远方

这是一篇关于切片的有用博客文章https://blog.golang.org/slices。它特别说明了这一点。重要的是要理解即使切片包含指针,它本身也是一个值。在幕后,它是一个包含指针和长度的结构值。它不是指向结构的指针。您看到的原因[40 50 50 50 50]是因为您更改了切片中的值,但您没有更改切片本身(它是 cap 和 len)
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go