GO语言容易犯错的问题

1. A\B\C\D 那个有语法错误?

type S struct {
}
func m(x interface{}) {
}
func g(x *interface{}) {
}
func main() {
    s := S{}
    p := &s
    m(s) //A
    g(s) //B
    m(p) //C
    g(p) //D
}
答案

B D 会编译错误
函数参数为 interface {} 时可以接收任何类型的参数,包括用户自定义类型等,
即使是接收指针类型也用 interface {},而不是使用 *interface {}
永远不要使用一个指针指向一个接口类型,因为它已经是一个指针。

2. 下面代码输出什么?

s1 := []int{1, 2, 3}
s2 := s1[1:]
s2[1] = 4
fmt.Println(s1)
s2 = append(s2, 5, 6, 7)
fmt.Println(s1)
答案

[1 2 4]
[1 2 4]
golang 中切片底层的数据结构是数组。当使用 s1 [1:] 获得切片 s2,和 s1 共享同一个底层数组
这会导致 s2 [1] = 4 语句影响 s1。
而 append 操作会导致底层数组扩容,生成新的数组,因此追加数据后的 s2 不会影响 s1

3. 下面代码输出什么?

func foo() (*int, error) {
    var i int = 5
    return &i, nil
}
func bar() {
    //use p
    fmt.Println(*p)
}
func main() {
    p, err := foo()
    if err != nil {
        fmt.Println(err)
        return
    }
    bar()
    fmt.Println(*p)
}
答案

bar 函数会发生 panic,空指针异常
因为 err 前面没有声明,所以 p, err := foo () 中的 p 是重新声明的局部变量,而不是我们在前面声明的全局变量 p

4. 下面代码输出什么?

func change(s ...int) {
    s = append(s, 3)
}
func main() {
    slice := make([]int, 5, 5)
    slice[0] = 1
    slice[1] = 2
    change(slice...)
    fmt.Println(slice)
    change(slice[0:2]...)
    fmt.Println(slice)
}
答案

[1 2 0 0 0]
[1 2 3 0 0]

5. 下列代码输出什么?

package main
import "fmt"
func main() {
    var m = map[string]int{
        "A": 21,
        "B": 22,
        "C": 23,
    }
    counter := 0
    for k, v := range m {
        if counter == 0 {
            delete(m, "A")
        }
        counter++
        fmt.Println(k, v)
    }
    fmt.Println("counter is ", counter)
    fmt.Println("m = ", m)

    var a = [5]int{1, 2, 3, 4, 5}
    var r [5]int

    for i, v := range a {
        if i == 0 {
            a[1] = 12
            a[2] = 13
        }
        r[i] = v
    }
    fmt.Println("r = ", r)
    fmt.Println("a = ", a)

    v := []int{1, 2, 3}
    for i := range v {
        v = append(v, i)
        fmt.Println(v)
    }
    // map遍历 - 会反映实时变化
    m1 := map[int]int{1: 1, 2: 2, 3: 3}
    for k := range m1 {
        m[fmt.Sprintf("%d", k+10)] = k // 添加的新元素可能会被遍历到
        fmt.Println(k, m)
    }
    ch := make(chan int)
    go func() {
        ch <- 1
        ch <- 2
        close(ch)
    }()
    for v := range ch {
        fmt.Println(v)
    }
}
答案

切片(slice)的range遍历特点:

  • 在遍历开始前,就把切片的长度固定下来了
  • 比如切片长度是3,那么就一定只遍历3次
  • 即使在遍历过程中append增加了元素,也不会影响遍历次数
  • 这是Go语言对切片遍历的特意设计,为了避免无限循环
    Map的range遍历特点:
  • 遍历过程中直接作用于当前map的实际状态
  • 如果遍历时删除了元素,后续就不会遍历到该元素
  • 如果遍历时添加了新元素,可能会在后续遍历中遇到这个新元素
  • map的这种特性让它能够在遍历时动态修改内容

总结特点:

  • string: 按Unicode字符遍历,只读
  • channel: 持续遍历直到关闭
  • array: 值拷贝遍历,固定次数
  • slice: 固定长度遍历
  • map: 直接引用遍历,可实时修改

6. 下列代码有什么问题?

func main() {
    var s []int
    s = append(s,1)
    var m map[string]int
    m["one"] = 1 
}
答案

切片可以开箱即用,但 map 需要用 make 函数 进行初始化之后才能赋值

7. 下列代码有什么问题?

func Stop(stop <-chan bool) {
        close(stop)
}

答案

  • 只有发送方应该关闭通道

    // 只读通道
    var readCh <-chan int
    // close(readCh) // 编译错误
    
  • 接收方不应该关闭通道

    // 只写通道
    var writeCh chan<- int
    close(writeCh) // 正确
    
  • 通道关闭后不能再发送数据,但可以继续接收数据

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容