duc*_*uck 32 pass-by-value go slice
在Golang,我正在尝试为我的旅行推销员问题制作一个争夺切片功能.虽然这样做我注意到当我开始编辑切片时,我给每个传递它时的加扰功能是不同的.
经过一些调试后我发现它是由于我编辑了函数内部的切片.但由于Golang应该是一种"通过价值传递"的语言,这怎么可能呢?
https://play.golang.org/p/mMivoH0TuV
我提供了一个游乐场链接来展示我的意思.通过删除第27行,您获得的输出与输入不同,这应该没有区别,因为该函数在作为参数传入时应该创建自己的切片副本.
有人可以解释这种现象吗?
icz*_*cza 82
是的,Go中的所有内容都按值传递.切片也是.但切片值是标题,描述了支持数组的连续部分,切片值仅包含指向实际存储元素的数组的指针.切片值不包括其元素(与数组不同).
因此,当您将切片传递给函数时,将从此标头创建一个副本,包括指针,该指针将指向相同的后备阵列.修改切片的元素意味着修改后备阵列的元素,因此共享相同后备阵列的所有切片将"观察"更改.
要查看切片标头中的内容,请查看reflect.SliceHeader类型:
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
Run Code Online (Sandbox Code Playgroud)
请参阅相关/可能重复的问题: Golang函数参数是否作为copy-on-write传递?
阅读博文:Go Slices:用法和内部
小智 11
您可以在下面找到一个示例。简而言之,切片也是按值传递的,但原始切片和复制的切片链接到相同的底层数组。如果此切片之一发生更改,则底层数组会发生更改,然后其他切片也会发生更改。
package main
import "fmt"
func main() {
x := []int{1, 10, 100, 1000}
double(x)
fmt.Println(x) // ----> 3 will print [2, 20, 200, 2000] (original slice changed)
}
func double(y []int) {
fmt.Println(y) // ----> 1 will print [1, 10, 100, 1000]
for i := 0; i < len(y); i++ {
y[i] *= 2
}
fmt.Println(y) // ----> 2 will print [2, 20, 200, 2000] (copy slice + under array changed)
}
Run Code Online (Sandbox Code Playgroud)
小智 7
Slice 可以通过值传递给函数,但是我们不应该使用append 来向函数中的slice 添加值,而应该直接使用赋值。原因是追加将创建新的内存并将值复制到其中。这是一个例子。
// Go program to illustrate how to
// pass a slice to the function
package main
import "fmt"
// Function in which slice
// is passed by value
func myfun(element []string) {
// Here we only modify the slice
// Using append function
// Here, this function only modifies
// the copy of the slice present in
// the function not the original slice
element = append(element, "blackhole")
fmt.Println("Modified slice: ", element)
}
func main() {
// Creating a slice
slc := []string{"rocket", "galaxy", "stars", "milkyway"}
fmt.Println("Initial slice: ", slc)
//slice pass by value
myfun(slc)
fmt.Println("Final slice: ", slc)
}
Output-
Initial slice: [rocket galaxy stars milkyway]
Modified slice: [rocket galaxy stars milkyway blackhole]
Final slice: [rocket galaxy stars milkyway]
Run Code Online (Sandbox Code Playgroud)
// Go program to illustrate how to
// pass a slice to the function
package main
import "fmt"
// Function in which slice
// is passed by value
func myfun(element []string) {
// Here we only modify the slice
// Using append function
// Here, this function only modifies
// the copy of the slice present in
// the function not the original slice
element[0] = "Spaceship"
element[4] = "blackhole"
element[5] = "cosmos"
fmt.Println("Modified slice: ", element)
}
func main() {
// Creating a slice
slc := []string{"rocket", "galaxy", "stars", "milkyway", "", ""}
fmt.Println("Initial slice: ", slc)
//slice pass by value
myfun(slc)
fmt.Println("Final slice: ", slc)
}
Output-
Initial slice: [rocket galaxy stars milkyway ]
Modified slice: [Spaceship galaxy stars milkyway blackhole cosmos]
Final slice: [Spaceship galaxy stars milkyway blackhole cosmos]
Run Code Online (Sandbox Code Playgroud)
当它传递时,切片与指向底层数组的指针一起传递,因此切片是一个指向底层数组的小结构。小结构被复制,但它仍然指向相同的底层数组。包含切片元素的内存块通过“引用”传递。保存容量、元素数量和指向元素的指针的切片信息三元组通过值传递。
\n\n处理传递给函数的切片的最佳方法(如果切片的元素被操作到函数中,并且我们不希望这反映在元素内存块上)是使用copy(s, *c)以下方式复制它们:
package main\n\nimport "fmt"\n\ntype Team []Person\ntype Person struct {\n Name string\n Age int\n}\n\nfunc main() {\n team := Team{\n Person{"Hasan", 34}, Person{"Karam", 32},\n }\n fmt.Printf("original before clonning: %v\\n", team)\n team_cloned := team.Clone()\n fmt.Printf("original after clonning: %v\\n", team)\n fmt.Printf("clones slice: %v\\n", team_cloned)\n}\n\nfunc (c *Team) Clone() Team {\n var s = make(Team, len(*c))\n copy(s, *c)\n for index, _ := range s {\n s[index].Name = "change name"\n }\n return s\n}\nRun Code Online (Sandbox Code Playgroud)\n\n但要小心,如果该切片包含sub slice进一步的复制,则需要进行进一步的复制,因为我们仍将共享指向相同内存块元素的子切片元素,示例如下:
package main\n\nimport "fmt"\n\ntype Team []Person\ntype Person struct {\n Name string\n Age int\n}\n\nfunc main() {\n team := Team{\n Person{"Hasan", 34}, Person{"Karam", 32},\n }\n fmt.Printf("original before clonning: %v\\n", team)\n team_cloned := team.Clone()\n fmt.Printf("original after clonning: %v\\n", team)\n fmt.Printf("clones slice: %v\\n", team_cloned)\n}\n\nfunc (c *Team) Clone() Team {\n var s = make(Team, len(*c))\n copy(s, *c)\n for index, _ := range s {\n s[index].Name = "change name"\n }\n return s\n}\nRun Code Online (Sandbox Code Playgroud)\n