Go指针、引用和解引用的规则:

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

Rule for Go Pointers, References, Dereferencing:

问题

我是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

}
英文:

I am new to GoLang, coming from the Delphi, C++ world - admittedly very excited about this language, which I think is destined to become "the next big thing".

I am trying to get a handle around how the Go parser and compiler handle pointers and references - can't seem to find any place where some clear rules are laid out.

In the below code sample for example, the return type *list.List and the local variable l are pointer types and require the pointer symbol * in their declarations, but they don't have to be dereferenced in use: l.PushBack(i). But in this same code the input parameter value *int64 is declared as a pointer and has to be dereferenced to be used properly: var i int64 = *value / 2

I assume that this is because list.List is a reference type, so the dereferencing is implicit when used, while int64 is a value type and must be handled just as any pointer to a value type, as in C++ for example: It must be dereferenced.

What is confusing to me is that even though *list.List has to be declared as a pointer type using *, when using the list instance, dereferencing is not required. This had me quite confused initially. Is that "just the way it is", or am I missing something?

Sample:

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

}

答案1

得分: 15

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() // 等同于 == (*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 总是按值传递。

英文:

All of the methods for a List have *List receivers: (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{}

In your example l is of type *List, so there's no need to dereference them.

Suppose, instead, that you had something like this:

type A struct{}
func (a  A) X() {
    fmt.Println("X")
}
func (a *A) Y() {
    fmt.Println("Y")
}

You are allowed to write:

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

Or you can do the following:

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

But it only works for method receivers. Go will not automatically convert function arguments. Given these functions:

func A(x *int) {
    fmt.Println(*x)
}
func B(y int) {
    fmt.Println(y)
}

This is invalid:

A(5)

You have to do this:

var x int 
A(&x)

This is also invalid:

var y *int
B(y)

You have to do this:

B(*y)

Unlike C# or Java, when it comes to structs, Go does not make a distinction between reference and value types. A *List is a pointer, a List is not. Modifying a field on a List only modifies the local copy. Modifying a field on a *List modifies all "copies". (cause they aren't copies... they all point to the same thing in memory)

There are types which seem to hide the underlying pointer (like a slice contains a pointer to an array), but Go is always pass by value.

huangapple
  • 本文由 发表于 2013年12月31日 08:16:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/20849751.html
匿名

发表评论

匿名网友

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

确定