Go 指针、引用、取消引用的规则:

Posted

技术标签:

【中文标题】Go 指针、引用、取消引用的规则:【英文标题】:Rule for Go Pointers, References, Dereferencing: 【发布时间】:2014-01-17 22:35:38 【问题描述】:

我是 GoLang 的新手,来自 Delphi、C++ 世界 - 无可否认,我对这种语言非常兴奋,我认为它注定会成为“下一件大事”。

我试图了解 Go 解析器和编译器如何处理指针和引用 - 似乎找不到任何明确规则的地方。

例如,在下面的代码示例中,返回类型*list.List 和局部变量l 是指针类型,在它们的声明中需要指针符号*,但它们在使用时不必被取消引用:l.PushBack(i)。但在同一代码中,输入参数value *int64 被声明为指针,必须取消引用才能正确使用:var i int64 = *value / 2

我认为这是因为list.List 是引用类型,所以在使用时取消引用是隐式的,而int64 是值类型,必须像任何指向值类型的指针一样处理,如在 C++ 中示例:它必须被取消引用。

令我困惑的是,即使 *list.List 必须使用 * 声明为指针类型,但在使用列表实例时,不需要取消引用。这让我一开始很困惑。是“就是这样”,还是我错过了什么?

示例:

func GetFactors(value *int64) *list.List 

    l := list.New()

    l.PushBack(*value)

    var i int64 = *value / 2

    for ; i > 1; i-- 

        if *value%i == 0 

            l.PushBack(i)

        
    

    return l


【问题讨论】:

不,Go 中没有 Java 愚蠢和类型种族主义。相反,当您调用“成员”函数时,会自动取消引用指针。 (这些函数实际上是不合时宜地定义的。) 【参考方案1】:

List 的所有方法都有*List 接收器:(http://golang.org/pkg/container/list/)

func (l *List) Back() *Element
func (l *List) Front() *Element
func (l *List) Init() *List
...
func (l *List) Remove(e *Element) interface

在您的示例中,l 的类型为 *List,因此无需取消引用它们。

假设你有这样的事情:

type A struct
func (a  A) X() 
    fmt.Println("X")

func (a *A) Y() 
    fmt.Println("Y")

你可以写:

a := A
a.X()
a.Y() // == (&a).Y()

或者您可以执行以下操作:

a := &A
a.X() // same like == (*a).X()
a.Y()

但它只适用于方法接收器。 Go 不会自动转换函数参数。鉴于这些功能:

func A(x *int) 
    fmt.Println(*x)

func B(y int) 
    fmt.Println(y)

这是无效的:

A(5)

你必须这样做:

var x int 
A(&x)

这也是无效的:

var y *int
B(y)

你必须这样做:

B(*y)

与 C# 或 Java 不同,当涉及到结构时,Go 不区分引用类型和值类型。 *List 是指针,List 不是。修改 List 上的字段只会修改本地副本。修改 *List 上的字段会修改所有“副本”。 (因为它们不是副本……它们都指向内存中的同一事物)

有些类型似乎隐藏了底层指针(就像切片包含指向数组的指针),但 Go 总是按值传递。

【讨论】:

好的——这对我来说终于有点意思了。我发现 go -it 看起来像 C/C++ 但它的行为不像... 我仍然感到困惑 - 请参阅 Move semantics in Go :我应该何时返回参考:*list.List 以及何时 list.List 一样好。我知道,如果我想“就地”修改一个值,那么我需要一个引用,但是例如在我的示例代码中,只有返回值是 list.List,除了返回一个引用,就像在我的示例中一样,我有什么收获吗? ,而不是 C++ 中的副本,如果不是 C++11 中新的“移动语义”? 它类似于 C# (msdn.microsoft.com/en-us/library/ms229017(v=vs.110).aspx) 中的值与引用类型。在这种情况下,您肯定想使用*List。尽管List 可能有效,但它只会复制第一个元素。链表的其余部分将被共享,如果您修改它可能会导致一些奇怪的行为。 学习 C/C++ 的最佳方法可能是对指针系统一无所知。一些取消引用的语言规范是here,并定义了何时发生隐式转换。例如:“与选择器一样,使用指针对具有值接收器的非接口方法的引用将自动取消对该指针的引用:pt.Mv 等价于 (*pt).Mv。” 为什么 go 隐式地将值转换为指针,以供期望指针的方法接收者使用?为什么不让编译器抱怨类型不匹配呢?这种“有时选择性隐式转换”让我作为 go 初学者感到困惑。

以上是关于Go 指针、引用、取消引用的规则:的主要内容,如果未能解决你的问题,请参考以下文章

调用 free() 包装器:取消引用类型双关指针将破坏严格别名规则

GCC 7,aligned_storage 和“取消引用类型双关指针将破坏严格别名规则”

go的值类型和引用类型1——传递和拷贝

引用的一些规则

go的值类型和引用类型2——内存分配规则

“运行时错误:无效的内存地址或无指针取消引用”创建表