关于golang:面试篇Go语言常见踩坑一

引言

本系列会列举一些在Go面试中常见的问题。

切片循环问题

For循环在咱们日常编码中可能用的很多。在很多业务场景中咱们都须要用for循环解决。但golang中的for循环在应用上须要留神一些问题,大家可否遇到。先看下边这一段代码:

func testSlice() {
    a := []int64{1,2,3}
    for _, v := range a {
        go func() {
            fmt.Println(v)
        }()
    }
    
    time.Sleep(time.Second)
}

output: 3 3 3

那么为什么会输入的是这个后果呢?

在golang的for循环中,循环外部创立的函数变量都是共享同一块内存地址,for循环总是应用同一块内存去接管循环中的的value变量的值。不论循环多少次,value的内存地址都是雷同的。咱们能够测试一下:

func testSliceWithAddress() {
    a := []int64{1,2,3}
    for _, v := range a {
        go func() {
            fmt.Println(&v)
        }()

    }

    time.Sleep(time.Second)
}

output:
        0xc0000b2008
        0xc0000b2008
        0xc0000b2008

合乎预期。如果大家比拟感兴趣的话能够去将这段代码的汇编打印进去,就能够发现循环的v始终在操作同一块内存。

同样的,在slice循环这块咱们还会遇见另一个乏味的中央,大家能够看看下边这段代码输入什么?

func testRange3() {
    a := []int64{1,2,3}
    for _, v := range a {
        a = append(a, v)
    }

    fmt.Println(a)
}

这段代码的输入后果是:[1 2 3 1 2 3],为什么呢?因为golang在循环前会先拷贝一个a,而后对新拷贝的a进行操作,所以循环的次数不会随着append而增多。

interface和nil比拟

比方返回了一个空指针,但并不是一个空interface

func testInterface() {
    doit := func(arg int) interface{} {
        var result * struct{} = nil
        if arg > 0 {
            result = &struct{}{}
        }

        return result
    }

    if res := doit(-1); res != nil {
        fmt.Println("result:", res)
    }
}

输入后果为:result: <nil>,为什么呢?因为在go里边变量有类型和值两个属性,在比拟的时候也会比拟类型和值都雷同才会认为相等。代码中result的类型是指针,值是nil,所以会有这样的输入。

可变参数是空接口类型

当参数的可变参数是空接口类型时,传入空接口的切片时须要留神参数开展的问题。

func testVolatile() {
    var a = []interface{}{1, 2, 3}

    fmt.Println(a)
    fmt.Println(a...)
}

输入后果为:

[1 2 3]
1 2 3

map遍历时程序不固定

不要置信map的程序!

func testMap() {
    m := map[string]string{
        "a": "a",
        "b": "b",
        "c": "c",
    }

    for k, v := range m {
        println(k, v)
    }
}

具体起因大家能够看一下源码:map.go:mapiterinit,就会发现下边这个代码用来决定从哪开始遍历map。另一个起因是map 在某些特定状况下(例如扩容),会产生key的搬迁重组。而遍历的过程,就是按程序遍历bucket,同时按程序遍历bucket中的key。搬迁后,key的地位产生了重大的变动,所以遍历map的后果就不可能按原来的程序了。

func mapiterinit(t *maptype, h *hmap, it *hiter) {
        ......
    // decide where to start
    r := uintptr(fastrand())
        ......
}

关注咱们

欢送对本系列文章感兴趣的读者订阅咱们的公众号,关注博主下次不迷路~

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理