指向指针的指针有什么好处?

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

What are pointers to pointers good for?

问题

在Go编程语言中,指向指针的指针如何变得有用?

(如果它们不是真正有用的话,为什么它们不是非法的?)

英文:

In the Go programming language; how can pointers to pointers become useful?

(Why are they not illegal if they are not really useful?)

答案1

得分: 32

任何数据类型的有用性取决于所解决的问题和解决问题的方法。如果一个数据类型不适合问题,那么它就不适合问题 - 没有其他更多的内容。

Go编程语言(以及大多数其他编程语言)基于程序员可以使用的简单规则来构建新的数据类型。其中一些规则是:

  • <code>*T</code>:创建一个指向T的新数据类型
  • <code>[10]T</code>:T的数组
  • <code>struct { t T; u U ... }</code>:包含T作为组件的结构体
  • ...

程序员可以通过组合这些简单规则来创建复杂的数据类型。可能的数据类型总数超过了有用的数据类型的数量。显然,存在(并且必须存在)一些根本没有用处的数据类型。这只是因为构建新数据类型的规则是简单的自然结果。

类型<code>**T</code>属于在程序中出现的可能性较小的类型。能够编写<code>*****T</code>并不意味着这种类型必须非常有用。


最后,回答你的问题

类型<code>**T</code>通常出现在我们希望将类型<code>T</code>的值的用户重定向到另一个类型<code>T</code>的值的上下文中,但由于某种原因我们无法访问值的所有用户或者找到用户的成本太高:

  1. 我们不想复制类型<code>T</code>的值(由于某种原因)
  2. 我们希望类型<code>T</code>的所有用户通过指针访问该值
  3. 我们希望快速重定向类型<code>T</code>的特定值的所有用户到另一个值

在这种情况下,使用<code>**T</code>是自然的,因为它允许我们在O(1)时间内实现第三步:

type User_of_T struct {
  Value **T
}

// 将类型T的特定值的所有用户重定向到另一个类型T的值。
func (u *User_of_T) Redirect(t *T) {
  *(u.Value) = t
}
英文:

The usefulness of any data type depends on the problem being solved and on the method used to solve the problem. If a data type does not fit the problem, it simply does not fit the problem - and there is nothing more to it.

The Go programming language (as well as most other programming languages) is based on simple rules that the programmer can use to build new data types. Some of these rules are:

  • <code>*T</code>: create a new data type that is a pointer to T
  • <code>[10]T</code>: an array of Ts
  • <code>struct { t T; u U ... }</code>: a structure which contains a T as a component
  • ...

The programmer can create complex data types by composing these simple rules. The total number of possible data types exceeds the number of useful data types. Clearly, there exist (and have to exist) data types which aren't useful at all. This is just a natural consequence of the fact that the rules for building new data types are simple.

The type <code>**T</code> falls into the category of types which are less probable to appear in a program. The fact that it is possible to write <code>*****T</code> doesn't imply that such a type has to be immensely useful.


And finally, the answer to your question:

The type <code>**T</code> usually appears in contexts where we want to redirect users of a value of type <code>T</code> to another value of type <code>T</code>, but for some reason we do not have access to all users of the value or finding the users would cost too much time:

  1. We do not want to copy values of type <code>T</code> (for some reason)
  2. We want all users of a value of type <code>T</code> to access the value via a pointer
  3. We want to quickly redirect all users of a particular value of type <code>T</code> to another value

In such a situation, using <code>**T</code> is natural because it allows us to implement the 3rd step in O(1):

type User_of_T struct {
  Value **T
}

// Redirect all users of a particular value of type T
// to another value of type T.
func (u *User_of_T) Redirect(t *T) {
  *(u.Value) = t
}

答案2

得分: 24

当你传递一个指向函数的指针时,函数会得到一个副本。所以给定指针赋予新值不会导致将其赋予原始值:

type Smartphone struct {
    name string
}

type Geek struct {
    smartphone *Smartphone
}

func replaceByNG(s **Smartphone) {
    *s = &Smartphone{"Galaxy Nexus"}
}

func replaceByIPhone(s *Smartphone) {
    s = &Smartphone{"IPhone 4S"}
}

func main() {
    geek := Geek{&Smartphone{"Nexus S"}}
    println(geek.smartphone.name)

    replaceByIPhone(geek.smartphone)
    println(geek.smartphone.name)

    replaceByNG(&geek.smartphone)
    println(geek.smartphone.name)
}

输出结果为:

Nexus S
Nexus S
Galaxy Nexus
英文:

When you pass a pointer to function, function gets a copy of it. So assigning new value to the given pointer will not result in assigning it to the original one:

type Smartphone struct {
	name string
}

type Geek struct {
	smartphone *Smartphone
}

func replaceByNG(s **Smartphone) {
	*s = &amp;Smartphone{&quot;Galaxy Nexus&quot;}
}

func replaceByIPhone(s *Smartphone) {
	s = &amp;Smartphone{&quot;IPhone 4S&quot;}
}

func main() {
    geek := Geek{&amp;Smartphone{&quot;Nexus S&quot;}}
    println(geek.smartphone.name)

    replaceByIPhone(geek.smartphone)
    println(geek.smartphone.name)

    replaceByNG(&amp;geek.smartphone)
    println(geek.smartphone.name)
}

The output is:

Nexus S
Nexus S
Galaxy Nexus

答案3

得分: 7

在C语言中,指向指针的情况非常常见。例如:

  • 多维数组(例如字符串数组,char** argv可能是最常见的例子)
  • 指针作为输出参数

然而,在Go语言中,指向指针的情况非常罕见。与通过指针访问数组不同,Go语言中有一个切片类型(在内部也存储了一个指针)。因此,在Go语言中,您仍然可以通过使用切片的切片来实现相同类型的间接访问,但是您通常不会看到类似**int的情况。

然而,第二个例子在Go程序中仍然适用。假设您有一个函数,应该能够更改作为参数传递的指针。在这种情况下,您将不得不传递指向该指针的指针,以便您可以更改原始指针。这在C语言中非常常见,因为函数只能返回一个值(通常是某种错误代码),如果您想返回一个额外的指针,您将不得不使用指向该指针的指针作为输出参数。然而,在Go语言中,函数可以返回多个值,因此指向指针的情况也很少见。但是它们仍然可能很有用,并且在某些情况下可能会导致更好的API。

例如,atomic.StorePointer函数可能是标准库中指向指针的罕见但隐藏的用例之一。

英文:

In C pointers to pointers are quite common. For example:

  • more dimensional arrays (for example an array of strings, char** argv might be the most prominent example here)
  • pointers as output parameters

In Go however, pointers to pointers are quite rare. Instead of accessing arrays by a pointer, there is a slice type (which also stores a pointer internally). So, you still might get the same kind of indirection by using a slice of slices in Go, but you normally won't see something like **int here.

The second example however might still apply to Go programs. Let's say you have a function which should be able to change a pointer passed as parameter. In that case, you will have to pass a pointer to that pointer, so that you can change the original pointer. That's extremely common in C, because functions can only return one value (which is often some kind of error code) and if you want to return an additional pointer, you will have to use a pointer to that pointer as output parameter. A function in Go however, can return multiple values, so the occurrences of pointers to pointers are also rare. But they might still be useful and might lead to a nicer API in some cases.

For example, the atomic.StorePointer function might be one of those rare but well hidden use-cases for pointers to pointers in the standard library.

答案4

得分: 4

Linus Torvalds最近提到了指向指针的指针如何导致具有良好品味的代码(在C语言中)。请参阅(其中之一)Brian Barto的博客文章

英文:

Linus Torvalds recently mentioned how pointers to pointers lead to code with good taste (in C). See (among others) Brian Barto's blog post.

答案5

得分: 3

这是一个涉及标准库中的 ** 的示例:

https://github.com/golang/go/blob/cfe2ab42e764d2eea3a3339aac1eaff97520baa0/src/encoding/gob/decoder.go#L20

英文:

Here is an example involving ** right in the standard library:

https://github.com/golang/go/blob/cfe2ab42e764d2eea3a3339aac1eaff97520baa0/src/encoding/gob/decoder.go#L20

答案6

得分: 0

例如,可以使用以下代码:

var target *string
setValue(&target, "示例字符串")

func setValue(target **string, value string) {
	if len(value) > 0 {
		*target = &value
	}
}

作为替代方案:

var target *string
target = setValue("示例字符串")

func setValue(value string) *string{
	if len(value) > 0 {
		return &value
	}
	return nil
}
英文:

For example with ** possible to use this code:

var target *string
setValue(&amp;target, &quot;пример строки&quot;)

func setValue(target **string, value string) {
	if len(value) &gt; 0 {
		*target = &amp;value
	}
}

as alternative to:

var target *string
target = setValue(&quot;пример строки&quot;)

func setValue(value string) *string{
	if len(value) &gt; 0 {
		return &amp;value
	}
	return nil
}

huangapple
  • 本文由 发表于 2012年1月7日 16:23:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/8768344.html
匿名

发表评论

匿名网友

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

确定