Go中的值语义和指针语义是什么意思?在本课程中,作者在解释数组和切片的内部结构时多次提到上述术语,我无法完全理解。
1 回答
当您调用一个函数或方法并将参数传递给它时,会从值中创建一个副本,并且该函数只能访问这些副本。
这意味着如果函数尝试修改/更改副本,它不会更改原始值。
例如:
func main() {
i := 1
fmt.Println("double:", double(i))
fmt.Println("original i:", i)
}
func double(i int) int {
i *= 2
return i
}
这个输出(在Go Playground上试试):
double: 2
original i: 1
即使double()
修改了它的i
参数,调用者的变量(其值被传递)也没有改变。
为了改变它,我们需要改变签名以期望一个指针,传递一个指针,并修改指向的值:
func main() {
i := 1
fmt.Println("double:", doublep(&i))
fmt.Println("original i:", i)
}
func doublep(i *int) int {
*i *= 2
return *i
}
这个输出(在Go Playground上试试):
double: 2
original i: 2
所以如果我们传递一些东西,我们期望如果传递的值被修改,原始值不会改变,除非我们传递一个指向它的指针。
指针语义意味着即使我们“按值”传递一些东西,被调用者仍然可以修改“原始”值,就像我们传递一个指向它的指针一样。
例如:
func main() {
is := []int{1, 2}
fmt.Println("double:", doubles(is))
fmt.Println("original is:", is)
}
func doubles(is []int) []int {
for i := range is {
is[i] *= 2
}
return is
}
这个输出(在Go Playground上试试):
double: [2 4]
original is: [2 4]
即使我们没有传递指针(is
不是指针),被调用者修改了它的元素,原始切片的值也改变了。
我们说即使在 Go 中一切都是按值传递的,但传递的 slice 具有指针语义,因为如果被调用者修改了元素,它会反映在原来的中。
推理
Go 中的所有内容都是按值传递的,切片也是如此。但是切片在底层是类似结构的数据结构,它包含一个指向包含实际元素的底层数组的指针。并且当您传递一个切片时,会制作一个副本,但只会复制这个切片头(这是切片值)。副本将持有相同的指针,指向相同的后备数组。不复制后备数组。所以当被调用者修改 slice 的元素时,backing array 的元素也被修改了,和原来 slice 的 backing array 是一样的。
在此处阅读更多相关信息:golang 切片是否按值传递?
有许多类型通过指针语义传递,例如切片、映射、通道。
值得注意的是——与切片不同——数组不在一行中,一个数组值意味着它的所有值,并且传递一个数组会复制它的所有元素。