zoukankan      html  css  js  c++  java
  • Go 语言陷阱

    https://geektutu.com/post/hpg-gotchas-array-slice.html

    源代码/数据集已上传到 Github - high-performance-go

     

    golang gotchas

    1 第一个陷阱

    1.1 下面程序的输出是

    1
    2
    3
    4
    5
    6
    7
    8
    9
    func foo(a [2]int) {
    a[0] = 200
    }

    func main() {
    a := [2]int{1, 2}
    foo(a)
    fmt.Println(a)
    }

    1.2 答案

    正确的输出是 [1 2],数组 a 没有发生改变。

    • 在 Go 语言中,数组是一种值类型,而且不同长度的数组属于不同的类型。例如 [2]int 和 [20]int 属于不同的类型。
    • 当值类型作为参数传递时,参数是该值的一个拷贝,因此更改拷贝的值并不会影响原值。

    我们在 切片(slice)性能及陷阱 这篇文章中也提到了,为了避免数组的拷贝,提高性能,建议传递数组的指针作为参数,或者使用切片代替数组。

    1.3 更多

    如果将上述程序替换为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    func foo(a *[2]int) {
    (*a)[0] = 200
    }

    func main() {
    a := [2]int{1, 2}
    foo(&a)
    fmt.Println(a)
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    func foo(a []int) {
    a[0] = 200
    }

    func main() {
    a := []int{1, 2}
    foo(a)
    fmt.Println(a)
    }

    输出将会变成 [200 2]

    在 切片(slice)性能及陷阱 这篇文章中,我们也提到了切片由三个值构成:

    • *ptr 指向底层数组的指针
    • len 长度
    • cap 容量

    因此,将切片作为参数时,拷贝了一个新切片,即拷贝了构成切片的三个值,包括底层数组的指针。对切片中某个元素的修改,实际上是修改了底层数组中的值,因此原切片也发生了改变。

    2 第二个陷阱

    2.1 下面程序的输出是

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    func foo(a []int) {
    a = append(a, 1, 2, 3, 4, 5, 6, 7, 8)
    a[0] = 200
    }

    func main() {
    a := []int{1, 2}
    foo(a)
    fmt.Println(a)
    }

    2.2 答案

    输出仍是 [1 2],切片 a 没有发生改变。

    传参时拷贝了新的切片,因此当新切片的长度发生改变时,原切片并不会发生改变。而且在函数 foo 中,新切片 a 增加了 8 个元素,原切片对应的底层数组不够放置这 8 个元素,因此申请了新的空间来放置扩充后的底层数组。这个时候新切片和原切片指向的底层数组就不是同一个了。因此,对新切片第 0 个元素的修改,并不会影响原切片的第 0 个元素。

    如果如果希望 foo 函数的操作能够影响原切片呢?

    两种方式:

    • 设置返回值,将新切片返回并赋值给 main 函数中的变量 a
    • 切片也使用指针方式传参。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    func foo(a []int) []int {
    a = append(a, 1, 2, 3, 4, 5, 6, 7, 8)
    a[0] = 200
    return a
    }

    func main() {
    a := []int{1, 2}
    a = foo(a)
    fmt.Println(a)
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    func foo(a *[]int) {
    *a = append(*a, 1, 2, 3, 4, 5, 6, 7, 8)
    (*a)[0] = 200
    }

    func main() {
    a := []int{1, 2}
    foo(&a)
    fmt.Println(a)
    }

    上述两个程序的输出均为:

    1
    [200 2 1 2 3 4 5 6 7 8]

    从可读性上来说,更推荐第一种方式

    去重

    func removeDuplicateElementForRule(addrs []string) []string {
      result := make([]string, 0, len(addrs))
      temp := map[string]struct{}{}
      for _, item := range addrs {
      if _, ok := temp[item]; !ok {
      temp[item] = struct{}{}
      result = append(result, item)
      }
      }
      return result
      }
     
  • 相关阅读:
    简单makeFile编写
    安装vim、简单linux指令
    XXX 不在sudoers文件中 解决方法
    MYSQL 5.7的那些坑
    testlink的那些坑
    MySQL 增删改查基础
    mysql操作之- 忘记root账户密码
    Python第三方库安装
    写在前面
    一、jenkins的使用 之 新建项目
  • 原文地址:https://www.cnblogs.com/cheyunhua/p/15455685.html
Copyright © 2011-2022 走看看