参数类型 *T 副本创建 (按引用传递)

type Duck struct {
    Age  int
    Name string
}
func passP(b *Duck) { //参数类型 *T 副本创建,修改原始变量的值
    b.Age++
    b.Name = "Great" + b.Name
    fmt.Printf("传入修改后的Bird:\t %+v, \t内存地址:%p, 指针的内存地址: %p\n", *b, b, &b)
}
func main() {
    parrot := &Duck{Age: 1, Name: "Blue"}
    fmt.Printf("原始的Bird:\t\t %+v, \t\t内存地址:%p, 指针的内存地址: %p\n", *parrot, parrot, &parrot)
    passP(parrot)
    fmt.Printf("调用后原始的Bird:\t %+v, \t内存地址:%p, 指针的内存地址: %p\n", *parrot, parrot, &parrot)
}

/*
原始的Bird:       {Age:1 Name:Blue},         内存地址:0xc0000044c0,   指针的内存地址: 0xc000006028
传入修改后的Bird:     {Age:2 Name:GreatBlue},     内存地址:0xc0000044c0, 指针的内存地址: 0xc000006038
调用后原始的Bird:     {Age:2 Name:GreatBlue},     内存地址:0xc0000044c0, 指针的内存地址: 0xc000006028
*/

可以看到在 passP 中,在 T 类型作为参数的时候,传递的 parrot 参数会创建指针的副本(指针的内存地址: 0xc000006038)和原始变量的指针地址(指针的内存地址:0xc000006028),都指向同一个内存(实际)地址(0xc0000044c0)。显然,在函数内对 T 的改变会影响到原始变量的值,因为它是在同一个对象的操作。

如何选择 T 还是 *T

  • 如果变量是数组或者结构体,使用类型 T 创建副本会影响性能,增加内存开销,可以使用指针类型 *T 传递
  • 如果使用类型 T ,Golang 编译器则尽量将对象分配到栈上,而 *T 则可能被分配到对象上,会影响垃圾回收
  • 如果不希望修改变量,就选择传递值类型 T ;如果希望修改原始变量的值,那么就选择指针类型 *T