41

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

 4 years ago
source link: https://studygolang.com/articles/29091
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
  • 布尔型,整型,指针
    布尔型和整型一般不用考虑指针类型,因为它们的对象很小,在内存上的开销可以忽略不计。只有想修改同一个变量的值才用指针传递。
  • 数组
    从内存和性能的角度上看,在函数间传递数组的开销是十分巨大的,当这个变量是一个数组时,不管这个数组有多长都会被完整复制,然后传递给函数
  • 字典,切片,通道
    这三个类型都是指向指针类型(指向一个底层的数据结构),可以当成是 *T 类型使用。

判断是否需要创建副本

  • 参数类型 T 副本创建 (按值传递)

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

    可以看到在 passV 函数中,在 T 类型作为参数的时候,传递的 parrot 参数会将它的副本(内存地址:0xc0000564a0)传递给 passV 函数,在这个函数内对参数的改变不会影响原始变量的地址(内存地址:内存地址:0xc000056440)

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

​ 【注】:请参考书中 《Go语言编程入门与实战技巧》P109页

欢迎关注我们的微信公众号,每天学习Go知识

FveQFjN.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK