golang中指针内容的差异

golang中指针内容的差异,go,Go,考虑以下结构和指针引用示例代码 package main import ( "fmt" "sync" ) type myStruct struct { mut *sync.RWMutex } type arrayOfMyStruct struct { structList []myStruct } func main() { k := &sync.RWMutex{} myStructInstance := myStruct{

考虑以下结构和指针引用示例代码

package main

import (
    "fmt"
    "sync"
)

type myStruct struct {
    mut *sync.RWMutex
}

type arrayOfMyStruct struct {
   structList []myStruct
}


func main() {
    k := &sync.RWMutex{}    
    myStructInstance := myStruct{k}
    fmt.Printf("%p", &k) // address 1 -> address of k
    fmt.Println("")
    fmt.Printf("%p", &*k) // address 2 -> address of what k points to
    fmt.Println("")

    var listStruct []myStruct

    listStruct = append(listStruct, myStructInstance)

    test := &arrayOfMyStruct{listStruct}

    test.access()
}

func (elem *arrayOfMyStruct) access() {
    mystructinstance := &elem.structList[0]
    mystructinstance2 := elem.structList[0]
    fmt.Println(&mystructinstance.mut) // address 3
    fmt.Println(&mystructinstance2.mut) // address 4
}

为什么地址2、3和4不同?它们不应该是相同的吗?

这是因为
myStruct
中的
mut
已经是一个指针
*sync.RWMutex
,所以当您这样做时:

&mystructinstance2.mut
实际上,您将获得一个指向
mut
的新指针,它是指向互斥体的指针,类似于
**sync.RWMutex
。只需删除打印语句上的
&
。当您使用
k:=&sync.RWMutex{}
创建互斥锁时,它已经是一个指针,因此您不应该在该变量上的任何位置使用
&
,否则它将创建指向该指针的新指针。您还可以使用
k:=new(sync.RWMutex)
创建指向新互斥体的指针

您可以使用Printf上的
%p
打印指针地址:

fmt.Printf("%p\n", mystructinstance2.mut)

地址1是指向互斥体的指针的地址。地址2是互斥锁的地址。也就是说,2点指向互斥位所在的位置,1点指向2中指针所在的位置。使用
%T
而不是
%p
打印将显示不同类型的值

append
实际上复制了您的结构。struct实例是一个值,就像
int
一样;将结构附加到列表时,实际上是在复制其中每个字段的值

结构的值语义在C和Go中广泛使用(也出现在例如C#ValueTypes中),但在Java、JavaScript或Python中不太常见。它们意味着您有时必须考虑事物是否是指针,但它们可以避免您在一个地方意外地进行更改而在其他地方产生影响(别名),并减少垃圾收集器必须遵循的指针数量

地址3是由
append
创建的结构副本中互斥指针的地址

mystructinstance2
的赋值也会复制该值,因此现在有三个副本在浮动。地址4是此新实例中互斥体指针的地址。

您需要

k := elem.structList[0].mut
p1 := &*k
或者,简单地说

p2 := &*elem.structList[0].mut
比如说,

package main

import (
    "fmt"
    "sync"
)

type myStruct struct {
    mut *sync.RWMutex
}

type arrayOfMyStruct struct {
    structList []myStruct
}

func main() {
    k := &sync.RWMutex{}
    myStructInstance := myStruct{k}
    fmt.Printf("%p\n", &*k)

    var listStruct []myStruct
    listStruct = append(listStruct, myStructInstance)

    test := &arrayOfMyStruct{listStruct}
    test.access()
}

func (elem *arrayOfMyStruct) access() {
    k := elem.structList[0].mut
    p1 := &*k
    fmt.Printf("%p\n", p1)
    p2 := &*elem.structList[0].mut
    fmt.Printf("%p\n", p2)
}
输出:

0xc4200142c0
0xc4200142c0
0xc4200142c0

Go的价值语义学可能会让你感到惊讶;
append
和变量赋值实际上是结构的副本。我将在回答中解释。他是否复制包含互斥体的对象并不重要,因为它是指向互斥体的指针,而不是对象中包含的互斥体本身。