【问题标题】:What is the difference between passing a struct and pointer of the struct, are they not both pointers?传递结构和结构的指针有什么区别,它们不是两个指针吗?
【发布时间】:2017-07-02 16:06:07
【问题描述】:

例如

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 *Vertex 和 myStruct Vertex 不都是指向结构本身的指针吗?为什么我在函数中修改结构时会出现行为差异?

golang 解析参数时是否在changeByValueStruct 中创建新结构?

【问题讨论】:

  • 结构体值是结构体值,不是指向结构体值的指针;指向结构值的指针是指针,而不是指向指针的指针。 Go 文档中的哪些内容给人以普通结构值是指针的印象?

标签: pointers go


【解决方案1】:

当您将指针作为参数传递时,幕后发生的事情是创建该指针的副本并将其传递给底层函数。它不应与传递引用相混淆。

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

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)
}

modifyValue函数内部发生的是Point结构的一个完全不同的实例被修改,所以调用函数时传递的值不受影响。

在第二个示例中,传递了一个指向结构的指针,因此可以以从外部可见的方式修改结构的字段。

最有趣的一点是最后一个函数modifyReference。如果您熟悉其他语言中可用的引用传递范例,您会期望能够完全修改引用的对象,但这不会发生。这是因为您正在修改作为参数传递的指针的副本。

您可能想知道,如果一切都是按值传递的,那么您应该何时传递指针以及何时传递值。传递值可确保调用者函数传递的结构不会受到任何更改,因此当您需要此行为时,请使用该值。这样做的缺点是会制作整个对象的副本,如果它太大,内存就会成为问题。

如果您将大结构作为参数传递,使用指针会更好,因为它可以节省空间,但您无法保证对象不会受到任何更改。

【讨论】:

  • 当结构包含更复杂的数据结构(如 gocv.Mat (godoc.org/gocv.io/x/gocv#Mat))时,这是否也适用?
  • @Sauraus 是的,取决于您如何在复杂类型中存储引用
  • @Nick 我一直在将结构按值传递给函数。间歇性我丢失了部分数据(丢失了具有非常大字符串的密钥)。这可能是因为“制作了整个对象的副本,如果它太大,内存就会成为问题。”?
  • “在 Go 中传递参数只有一种方式,那就是按值。”这种说法是错误的,可悲的是被许多人使用。映射、切片和指针是引用类型。
【解决方案2】:

将结构传递给函数参数会复制值。并且传递结构的指针不会。所以传递struct不能更新字段值。

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

【讨论】:

  • 但是内部包含结构的变量只是指向结构的指针,对吗?
  • 正如我上面所说,传递结构复制值。
  • 所以它创建了一个新的结构,它具有相同的值和不同的地址?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-28
  • 2014-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多