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

Pointers 指针上的golang指针作为函数参数,pointers,go,Pointers,Go,我有以下功能: func addCatsToMap(m map[string][]CatHouse, meowId int, treats Set, dog *Dog) { //if (complicated thing) add Cat to m } 其中,Set,treats的类型是具有以下定义的接口: type Set interface { Add(value string) Contains(value string) (bool) Length() (int)

我有以下功能:

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

//if (complicated thing) add Cat to m

}
其中,
Set
treats
的类型是具有以下定义的接口:

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

m
treats
dog
是通过引用传递的,并且
meowId
复制了它的值,这是真的吗

我假设:

  • m
    是通过引用传递的,因为它是一个映射
  • dog
    是一个结构。因此,我应该传递指针以避免复制数据

参考传递是一种语言,Go中没有什么是“参考传递”。通过引用传递意味着赋值运算符可以在单独使用时更改原始值。但是,有些引用类型,如映射和指针,指向某个地方。除非您使用其他操作符,如映射索引和
*
操作符,否则对其使用赋值操作符不会修改原始值

您的map
m
是一种引用类型,因此类似于指针,这是正确的。除替换地图外,对地图的任何更改都将修改原始地图

m["whatever"] = 2           // Modifies the original map
m = anothermap              // Does not modify the original map
如果存在真实的“通过引用传递”,则第二个示例将修改原始地图

传递一个指针,就像使用
dog
一样,允许您修改原始文件。如果调用任何指针方法或使用
*
运算符,则原始值将更改。在您的示例中,可能不需要指针。如果
Dog
很小,只需传递一份副本可能会更容易。由程序员决定何时是使用指针的好时机

Set
不是通过引用传递的接口不是引用。虽然在6g编译器内部,接口确实使用指针,但接口本身的行为与指针不同。传递一个接口,不管它包含的对象大小,都和使用6g编译器传递指针一样便宜。但是,无法像使用指针和映射那样修改接口的原始值


虽然不能修改传递的原始接口,但该接口可以包含指针类型。在这种情况下,它的行为就像狗指针一样,在这里调用某些方法可以修改原始的。对于特定的
Set
接口,我猜它包含基于方法名称的指针类型。因此,当您调用
set.Add(无论什么)
时,它将更改原始的内部数据。

接口类型只是一组方法。请注意,接口定义的成员不指定接收器类型是否为指针。这是因为值类型的方法集是其关联指针类型的方法集的子集。那是一口。我的意思是,如果你有以下几点:

m["whatever"] = 2           // Modifies the original map
m = anothermap              // Does not modify the original map
type Whatever struct {
    Name string
}
您可以定义以下两种方法:

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

func (w Whatever) Bar() {
    ...
}
然后类型
Whatever
只有方法
Bar()
,而类型
*Whatever
有方法
Foo()
Bar()
。这意味着如果您具有以下界面:

type Grits interface {
    Foo()
    Bar()
}
然后
*which
实现
砂砾
但是
which
没有,因为
which
缺少
Foo()方法。当您将函数的输入定义为接口类型时,您不知道它是指针类型还是值类型

以下示例说明了一个函数,该函数以两种方式接受接口类型:

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)
}
您可以调用
Raname(&f,“Jorelli果”)
,但不能
重命名(c,“Jorelli条”)
,因为
*果
都实现
可重命名
,而
*糖果
实现
可重命名
,而
糖果
不实现

在函数调用中,函数值和参数在 通常的顺序。在对它们求值之后,调用的参数 按值传递给函数,被调用函数开始 执行。函数的返回参数按值传递 当函数返回时返回到调用函数

与C系列中的所有语言一样, Go中的所有内容都是按值传递的。也就是说,函数总是得到一个 正在传递的对象的副本,好像有一个任务 将值指定给参数的语句。比如传球 函数的int值生成int的副本,并传递 指针值复制指针,但不复制指针所指向的数据 到(有关这如何影响方法的讨论,请参见下一节。) 接收器。)

映射和切片值的行为类似于指针:它们是 包含指向基础映射或切片数据的指针。复制地图或 切片值不会复制它指向的数据。复制接口 值生成存储在接口值中的对象的副本。如果 接口值保存一个结构,复制接口值会生成一个 结构的副本。如果接口值包含指针,则复制 接口值生成指针的副本,但同样不是指针的副本 它指向的数据