为什么在一个函数中有些值会被更新,而其他值则不会被更新?

huangapple go评论78阅读模式
英文:

Why do some values get updated in a func and others don't?

问题

比较这两个例子:

(来自:https://goinbigdata.com/golang-pass-by-pointer-vs-pass-by-value/)

package main

import "fmt"

type Person struct {
    firstName string
    lastName  string
}

func changeName(p Person) {
    p.firstName = "Bob"
}

func main() {
    person := Person {
        firstName: "Alice",
        lastName: "Dow",
    }

    changeName(person)

    fmt.Println(person)
}

上面的代码返回 {Alice Dow},所以结构体没有改变。

现在看这个例子:

package main

import "fmt"

func main() {
    slice := []string{"a", "b", "c"}

    fmt.Println(slice)
    updateSlice(slice)
    fmt.Println(slice)
}

func updateSlice(slice []string) []string {
    slice[0] = "x"
    return slice
}

输出结果是:

[a b c]
[x b c]

所以函数 updateSlice 改变了切片。

有人能解释一下这两个例子的区别吗?

英文:

Compare these two examples

(from: https://goinbigdata.com/golang-pass-by-pointer-vs-pass-by-value/)

package main

import "fmt"

type Person struct {
    firstName string
    lastName  string
}

func changeName(p Person) {
    p.firstName = "Bob"
}

func main() {
    person := Person {
        firstName: "Alice",
        lastName: "Dow",
    }

    changeName(person)

    fmt.Println(person)
}

The code above returns {Alice Dow} so the struct is not changed.

Now in this example

package main

import "fmt"

func main() {
	slice := []string{"a", "b", "c"}

	fmt.Println(slice)
	updateSlice(slice)
	fmt.Println(slice)
}

func updateSlice(slice []string) []string {
	slice[0] = "x"
	return slice
}

the output is

[a b c]
[x b c]

so the slice was changed by the func updateSlice.

Would someone explain the difference?

答案1

得分: 1

切片是对底层数组的引用。因此,当你将切片传递给函数时,实际上是传递了对底层数组的引用。

在第一个代码片段中,当你调用changeName(person)时,Go 会创建一个 person 结构体的副本并将其传递给changeName函数。

func main() {
    person := Person {
        firstName: "Alice",
        lastName: "Dow",
    }

    changeName(person) //Go 会传递 person 结构体的副本

    fmt.Println(person)
}

在 Go 中,切片只是对底层数组的引用;切片不是数组。在第二个示例中,updateSlice(slice)也是按值传递。它创建了slice的副本。然而,在这种情况下,Go 创建的是对数组的引用的副本。这意味着,“slice 的副本”和“slice”都指向同一个底层数组。

func main() {
    slice := []string{"a", "b", "c"}

    fmt.Println(slice)
    //这里仍然是按值传递,但传递的值是一个引用
    //即复制的值仍然指向同一个底层数组
    updateSlice(slice)
    fmt.Println(slice)
}

如果你想比较一下 Go 中的切片与其他语言(如 JavaScript 或 Ruby)中的切片函数的工作方式,请不要这样做(我在学习 Go 时犯过这个错误,所以提一下)。

为了更加明确,当你执行slice[0] = "x"时,你并没有修改切片本身;你修改的是切片所引用的底层数组。

英文:

A slice is a reference to an underlying array. So, when you pass a slice to a function, you are essentially passing a reference to the underlying array.

In the first code snippet, when you call changeName(person), Go will create a copy of the person struct and pass it to the changeName function.

func main() {
    person := Person {
        firstName: "Alice",
        lastName: "Dow",
    }

    changeName(person) //Go will pass a copy of the person struct

    fmt.Println(person)
}

A slice in Go is just a reference to an underlying array; a slice is not an array. In the second example, updateSlice(slice) also passes by value. It creates a copy of slice. However, in this case, Go is creating a copy of a reference to an array. That means, that the "copy of slice" and "slice" are both pointing to the same underlying array.

func main() {
    slice := []string{"a", "b", "c"}

    fmt.Println(slice)
    //Go is still passing by value here, but the value passed is a reference
    // i.e. the copied value still points to the same underlying array
    updateSlice(slice)
    fmt.Println(slice)
}

Just in case you are trying to compare how a slice function in other languages like Javascript or Ruby work to a Slice in Go, don't (mentioning this because I did that when I was learning Go).

To add a little more clarification, when you do slice[0] = "x", you are not modifying the slice; you are modifying the underlying array that slice is referencing.

huangapple
  • 本文由 发表于 2021年9月15日 08:14:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/69185739.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定