Golang 学习笔记:参数类型传递机制

  • 参数类型 *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