代码之家  ›  专栏  ›  技术社区  ›  Alana Storm

函数参数为接口时复制的内容

  •  3
  • Alana Storm  · 技术社区  · 8 年前

    假设:在go中,所有函数参数都是按值传递的。为了获得通过引用传递的语义/性能,go程序员通过指针传递值。Go仍然会复制这些参数,但它复制的指针有时比复制实际参数的内存效率更高。

    问题:当您传递一个接口时,会发生什么?i、 例如,在这样的程序中

    package main
    import "fmt"
    
    type Messages struct {
        hello string
    }
    
    func main() {
      sayHelloOne(Messages{"hello world"});
      sayHelloTwo(&Messages{"hello world"});
      sayHelloThree(Messages{"hello world"});
    }
    
    
    //go makes a copy of the struct
    func sayHelloOne(messages Messages) {
      fmt.Println(messages.hello)
    }
    
    //go makes a *pointer* to the struct
    func sayHelloTwo(messages *Messages) {
      fmt.Println(messages.hello)
    }
    
    //go --- ???
    func sayHelloThree(messages interface{}) {
      fmt.Println(messages.(Messages).hello)
    }
    

    当程序员调用 sayHelloThree 作用是 messages 正在复制?或者它是指向 消息 复制了吗?还是有什么奇怪的事让我推迟到 消息 是演员吗?

    1 回复  |  直到 8 年前
        1
  •  4
  •   Adrian    8 年前

    将复制接口值。接口值包括基础类型描述符和基础值,基础值可以是满足接口的任何类型(指针或其他类型)。在接口中“包装”对这些语义没有影响。您可以在自己引用的代码中看到这一点:您必须将类型断言为 Messages 价值 ,不是 *Messages 指针 。另一方面,如果你通过了 *消息 在中,作为参数,这是函数内部的内容。

    这很容易通过实验证明:

    m := Messages{"hello world"}
    var mi interface{}
    mi = m
    m.hello = "wait what?"
    fmt.Println(mi.(Messages).hello)
    // hello world
    

    工作操场示例: https://play.golang.org/p/lnVzr79eUZF

    此外,要注意像“复制指针比复制实际参数更节省内存”这样的泛化,这并不是普遍正确的,语义的变化往往比资源使用模式的变化更重要。例如,当值小于操作体系结构上的地址时,指针的效率明显较低。指针还可以将值强制到堆而不是堆栈上,这会增加GC的压力,因为GC会忽略堆栈上的值,而不管它对内存使用的影响如何。