简体   繁体   English

指针上的golang指针作为函数参数

[英]golang pointers on pointers as function parameters

I have the following function: 我有以下功能:

func addCatsToMap(m map[string][]CatHouse, meowId int, treats Set, dog *Dog) {

//if (complicated thing) add Cat to m

}

where Set , the type of treats , is an interface with the following definition: 其中Set的类型, treats ,是具有以下定义的接口:

type Set interface {
  Add(value string)
  Contains(value string) (bool)
  Length() (int)
  RemoveDuplicates()
}

Question: 题:

Is it true that m , treats , and dog are passed-by-reference , and meowId has it's value copied? mtreatsdog通过引用传递的 ,并且meowId是否已复制其值,这是真的吗?

I assume that: 我认为:

  • m is pass-by-reference because it's a map m是传递参考,因为它是一张地图
  • dog is a struct. dog是一种结构。 So, I should pass the pointer to avoid copying the data 因此,我应该传递指针以避免复制数据

An interface type is simply a set of methods. 接口类型只是一组方法。 Notice that the members of an interface definition do not specify whether or not the receiver type is a pointer. 请注意,接口定义的成员未指定接收方类型是否为指针。 That is because the method set of a value type is a subset of the method set of its associated pointer type . 这是因为值类型的方法集是其关联的指针类型的方法集的子集 That's a mouthful. 满嘴 What I mean is, if you have the following: 我的意思是,如果您具有以下条件:

type Whatever struct {
    Name string
}

and you define the following two methods: 然后定义以下两种方法:

func (w *Whatever) Foo() {
    ...
}

func (w Whatever) Bar() {
    ...
}

Then the type Whatever has only the method Bar() , while the type *Whatever has the methods Foo() and Bar() . 那么类型Whatever仅具有方法Bar() ,而类型*Whatever具有方法Foo()Bar() That means if you have the following interface: 这意味着如果您具有以下界面:

type Grits interface {
    Foo()
    Bar()
}

Then *Whatever implements Grits but Whatever does not, because Whatever lacks the method Foo() . 然后*Whatever实现GritsWhatever都不实现,因为Whatever缺少Foo()方法。 When you define the input to a function as an interface type, you have no idea whether it's a pointer or a value type. 当您将函数的输入定义为接口类型时,您不知道它是指针还是值类型。

The following example illustrates a function that takes an interface type in both ways: 下面的示例说明了一种采用两种方式都采用接口类型的函数:

package main

import "fmt"

type Fruit struct {
    Name string
}

func (f Fruit) Rename(name string) {
    f.Name = name
}

type Candy struct {
    Name string
}

func (c *Candy) Rename(name string) {
    c.Name = name
}

type Renamable interface {
    Rename(string)
}

func Rename(v Renamable, name string) {
    v.Rename(name)
    // at this point, we don't know if v is a pointer type or not.
}

func main() {
    c := Candy{Name: "Snickers"}
    f := Fruit{Name: "Apple"}
    fmt.Println(f)
    fmt.Println(c)
    Rename(f, "Zemo Fruit")
    Rename(&c, "Zemo Bar")
    fmt.Println(f)
    fmt.Println(c)
}

you could call Raname(&f, "Jorelli Fruit") but not Rename(c, "Jorelli Bar") , because both Fruit and *Fruit implement Renamable , while *Candy implements Renable and Candy does not. 您可以调用Raname(&f, "Jorelli Fruit")而不是Rename(c, "Jorelli Bar") ,因为Fruit*Fruit实现Renamable ,而*Candy实现RenableCandy没有。

http://play.golang.org/p/Fb-L8Bvuwj http://play.golang.org/p/Fb-L8Bvuwj

Pass by reference is a language thing, nothing in Go is "pass by reference". 引用传递是一种语言,Go中没有什么是“引用传递”。 Pass by reference means the assignment operator can change the original value when use alone. 通过引用传递意味着赋值运算符可以在单独使用时更改原始值。 However, there are reference types such as maps and pointers which point somewhere. 但是,有些引用类型(例如映射和指针)指向某处。 Using the assignment operator on them will not modify the original unless you use other operators such as the map index and the * operator. 除非您使用地图索引和*运算符之类的其他运算符,否则在它们上使用赋值运算符将不会修改原始运算符。

You are correct that your map m is a reference type and therefore like a pointer. 您是正确的,您的地图m是引用类型,因此就像一个指针。 Any changes to the map except replacing the map will modify the original. 除了替换地图以外,对地图的任何更改都会修改原始地图。

m["whatever"] = 2           // Modifies the original map
m = anothermap              // Does not modify the original map

If there was true "pass by reference", the second example would modify the original map. 如果存在真实的“通过引用传递”,第二个示例将修改原始地图。

Passing a pointer, as you do with dog allows you to modify the original. 像使用dog一样,传递指针可让您修改原始文件。 If you call any pointer methods or use the * operator, the original will change. 如果调用任何指针方法或使用*运算符,则原始格式将发生变化。 In your example, a pointer may not have been needed. 在您的示例中,可能不需要指针。 If Dog is small, it may be easier to just pass a copy. 如果Dog小,则仅传递副本可能会更容易。 It is up to the programmer to determine when it is a good time to use a pointer. 由程序员决定何时是使用指针的好时机。

Set is not passed by reference. Set不通过引用传递。 Interfaces are not references. 接口不是参考。 While it is true that internally in the 6g compiler an interface uses pointers, the interface itself does not act like one. 虽然在6g编译器内部确实有一个接口使用了指针,但该接口本身却不像指针。 Passing an interface, no matter of the size of the object it contains, is as cheap as passing a pointer using the 6g compiler. 不管接口包含的对象大小如何,传递接口与使用6g编译器传递指针一样便宜。 However, there is no way to modify the original value of an interface as you can with pointers and maps. 但是,没有办法像使用指针和映射那样修改接口的原始值。

Although you can not modify the original interface passed, the interface can contain a pointer type. 尽管您不能修改传递的原始接口,但该接口可以包含指针类型。 In that case it would act just like the dog pointer where the calling of certain methods can modify the original. 在这种情况下,它的作用就像狗指针,其中某些方法的调用可以修改原始方法。 For your particular Set interface, I would guess it contains a pointer type based on the method names. 对于您的特定Set接口,我想它包含基于方法名称的指针类型。 So when you call set.Add(whatever) , it will change the internal data of the original. 因此,当您调用set.Add(whatever) ,它将更改原始文件的内部数据。

Calls, The Go Programming Language Specification 调用,Go编程语言规范

In a function call, the function value and arguments are evaluated in the usual order. 在函数调用中,函数值和参数按通常的顺序求值。 After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. 在对它们进行评估之后,调用的参数将按值传递给函数,并且被调用函数开始执行。 The return parameters of the function are passed by value back to the calling function when the function returns. 当函数返回时,该函数的返回参数按值传递回调用函数。

When are function parameters passed by value? 函数参数何时按值传递? FAQ - The Go Programming Language. 常见问题解答-Go编程语言。

As in all languages in the C family, everything in Go is passed by value. 与C系列中的所有语言一样,Go中的所有内容都按值传递。 That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. 也就是说,函数总是获得所传递事物的副本,就像有一个赋值语句将值分配给参数一样。 For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the pointer, but not the data it points to. 例如,将int值传递给函数将构成int的副本,而将指针值传递将使指针具有副本,但不会复制其指向的数据。 (See the next section for a discussion of how this affects method receivers.) (有关如何影响方法接收者的讨论,请参见下一节。)

Map and slice values behave like pointers: they are descriptors that contain pointers to the underlying map or slice data. 映射和切片值的行为类似于指针:它们是包含指向基础映射或切片数据的指针的描述符。 Copying a map or slice value doesn't copy the data it points to. 复制地图或切片值不会复制其指向的数据。 Copying an interface value makes a copy of the thing stored in the interface value. 复制接口值将复制存储在接口值中的事物。 If the interface value holds a struct, copying the interface value makes a copy of the struct. 如果接口值包含一个结构,则复制接口值将复制该结构。 If the interface value holds a pointer, copying the interface value makes a copy of the pointer, but again not the data it points to. 如果接口值包含一个指针,则复制接口值将复制该指针,但不会复制它指向的数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM