传递结构体和传递结构体指针之间有什么区别?它们不都是指针吗?

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

What is the difference between passing a struct and pointer of the struct, are they not both pointers?

问题

例如

var myStructRef *Vertex
var myStruct Vertex
myStructRef = &Vertex{2, 3}
myStruct = Vertex{2, 3}

fmt.Println(myStructRef)
fmt.Println(myStruct)
changeByReferenceStruct(myStructRef)
changeByValueStruct(myStruct)
fmt.Println(myStructRef)
fmt.Println(myStruct)

func changeByValueStruct(myStruct Vertex) {
    myStruct.X = 5
    fmt.Println(myStruct)
}

func changeByReferenceStruct(myStruct *Vertex) {
    myStruct.X = 7
    fmt.Println(myStruct)
}

当我在函数中修改结构体时,为什么myStructRef *VertexmyStruct Vertex的行为不一致?Go语言是否在解析参数时在changeByValueStruct中创建了一个新的结构体?

英文:

For example

var myStructRef *Vertex
var myStruct Vertex
myStructRef = &Vertex{2, 3}
myStruct = Vertex{2, 3}

fmt.Println(myStructRef)
fmt.Println(myStruct)
changeByReferenceStruct(myStructRef)
changeByValueStruct(myStruct)
fmt.Println(myStructRef)
fmt.Println(myStruct)

And

func changeByValueStruct(myStruct Vertex) {
    myStruct.X = 5
    fmt.Println(myStruct)
}


func changeByReferenceStruct(myStruct *Vertex) {
    myStruct.X = 7
    fmt.Println(myStruct)
}

Isn't both myStructRef *Vertex and myStruct Vertex a pointer pointing to the struct itself? Why is there a discrepancy in behavior when I modify the struct in a function?

Is golang creating a new struct in changeByValueStruct when it resolves the parameter?

答案1

得分: 118

当你将指针作为参数传递时,在底层会创建指针的副本并传递给底层函数。这与按引用传递是不同的。

让我们看一个例子来更好地理解:

package main

import (
	"fmt"
)

type Point struct {
	x int
	y int
}

func (p Point) String() string {
	return fmt.Sprintf("(%d, %d)", p.x, p.y)
}

func modifyValue(point Point) {
	point.x += 10
}

func modifyPointer(point *Point) {
	point.x = 5
	point.y = 5
}

func modifyReference(point *Point) {
	point = &Point{5, 5}
}

func main() {
	p := Point{0, 0}
	fmt.Println(p) // 输出 (0, 0)
	
	modifyValue(p)
	fmt.Println(p) // 输出 (0, 0)
	
	modifyPointer(&p)
	fmt.Println(p) // 输出 (5, 5)
	
	p = Point{0, 0}
	modifyReference(&p)
	fmt.Println(p) // 输出 (0, 0)
}

modifyValue 函数内部发生的是对一个完全不同的 Point 结构的修改,因此在调用函数时传递的值不受影响。

在第二个例子中,传递了一个指向结构体的指针,因此可以修改结构体的字段,这种修改在外部是可见的。

最有趣的一点是最后一个函数 modifyReference。如果你熟悉其他语言中的按引用传递范式,你可能期望能够修改所引用的对象,但实际上并不会发生这种情况。这是因为你修改的是作为参数传递的指针的副本

你可能会想,既然一切都是按值传递,那么什么时候应该传递指针,什么时候应该传递值呢?传递值可以确保调用函数时传递的结构体不会发生任何改变,所以当你需要这种行为时,选择传递值。这种方式的缺点是会创建整个对象的副本,如果对象太大,内存就会成为一个问题。

如果你将一个大的结构体作为参数传递,使用指针会更好,因为它节省空间,但你失去了对象不会发生任何改变的保证。

英文:

When you pass a pointer as an argument, what happens under the hood is that a copy of that pointer is created and passed to the underlying function. It should not be confused with pass-by-reference.

Let's look at an example to better grasp it:

package main

import (
	"fmt"
)

type Point struct {
	x int
	y int
}

func (p Point) String() string {
	return fmt.Sprintf("(%d, %d)", p.x, p.y)
}

func modifyValue(point Point) {
	point.x += 10
}

func modifyPointer(point *Point) {
	point.x = 5
	point.y = 5
}

func modifyReference(point *Point) {
	point = &Point{5, 5}
}

func main() {
	p := Point{0, 0}
	fmt.Println(p) // prints (0, 0)
	
    modifyValue(p)
	fmt.Println(p) // prints (0, 0)
	
    modifyPointer(&p)
	fmt.Println(p) // prints (5, 5)
	
    p = Point{0, 0}
    modifyReference(&p)
	fmt.Println(p) // prints (0, 0)
}

What happens inside the modifyValue function is that a totally different instance of a Point structure is modified, so the value passed when calling the function is unaffected.

In the second example, a pointer to the structure is passed so the fields of the structure can be modified in a way that is visible from outside.

The most interesting point is made by the last function, modifyReference. If you are familiar with the pass by reference paradigm available in other languages you would expect to be able to modify the referenced object altogether, but this doesn't happen. It's because you're modifying a copy of the pointer passed as argument.

You may wonder, if everything is passed by value, when should you pass pointers and when values. Passing values assures the caller function that the passed structure cannot suffer any changes, so when you need this behaviour, go for the value. The downside of this is that a copy of the entire object is made and, if it is too big, memory becomes a concern.

If you're passing a big structure as an argument, using a pointer is better because it saves space, but you lose the guarantee that the object won't suffer any changes.

答案2

得分: 6

将结构体传递给函数参数会复制其值。而传递结构体的指针则不会复制。因此,传递结构体无法更新字段的值。

package main

import (
	"fmt"
)

type Foo struct {
	value int
}

func PassStruct(foo Foo) {
	foo.value = 1
}

func PassStructPointer(foo *Foo) {
	foo.value = 1
}

func main() {
	var foo Foo

	fmt.Printf("before PassStruct: %v\n", foo.value)
	PassStruct(foo)
	fmt.Printf("after PassStruct: %v\n", foo.value)
	
	fmt.Printf("before PassStructPointer: %v\n", foo.value)
	PassStructPointer(&foo)
	fmt.Printf("after PassStructPointer: %v\n", foo.value)
}

链接:https://play.golang.org/p/AM__JwyaJa

英文:

Passing struct to function argument makes copy of values. And passing pointer of struct doesn't. So passing struct can't update field value.

package main

import (
	"fmt"
)

type Foo struct {
	value int
}

func PassStruct(foo Foo) {
	foo.value = 1
}

func PassStructPointer(foo *Foo) {
	foo.value = 1
}

func main() {
	var foo Foo

	fmt.Printf("before PassStruct: %v\n", foo.value)
	PassStruct(foo)
	fmt.Printf("after PassStruct: %v\n", foo.value)
	
	fmt.Printf("before PassStructPointer: %v\n", foo.value)
	PassStructPointer(&foo)
	fmt.Printf("after PassStructPointer: %v\n", foo.value)
}

https://play.golang.org/p/AM__JwyaJa

huangapple
  • 本文由 发表于 2017年7月3日 00:06:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/44872739.html
匿名

发表评论

匿名网友

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

确定