本文参考自 脑子进煎鱼了 大佬 公众号

本文为脑子进煎鱼了 文章思考与感悟, 点此访问

问题1

在程序中定义了一个map,然后直接使用

    var m map[string]string
    m["name"] = "xiaosheng"

程序会发生报错
如下:

 panic: assignment to entry in nil map
 // ·`翻译一下`·
 Panic:对空映射中的入口的赋值

解决方法

需要将其进行初始化,使用make为其分配空间,初始化

    m := make(map[string]string)
    m["name"] = "xiaosheng"

问题2:引用空指针

type Point struct {
    X, Y int
}

func (p *Point) Add() int {
    return p.X + p.Y
}
func test2(){
    var p *Point
    fmt.Println(p.Add())
}

这个代码运行也会报错

PS D:\Go\template_learn\c03> go run .\test.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0x98c556]

goroutine 1 [running]:
main.(*Point).Add(...)
D:/Go/template_learn/c03/test.go:24
main.test2()
D:/Go/template_learn/c03/test.go:28 +0x16
main.main()
D:/Go/template_learn/c03/test.go:16 +0x17
exit status 2

原因是p是一个指针,指针必须初始化才能进行调用

解决方法

// 初始化
    var p *Point = new(Point)
    fmt.Println(p.Add())
// 用值对象
    var p Point
    fmt.Println(p.Add())

问题3:循环迭代器变量引用

循环迭代器变量是一个单一的变量,在每个循环迭代中取不同的值

下面结果会输出什么:………??

func test3() {
    var out []*int
    for i := 0; i < 3; i++ {
        out = append(out, &i)
    }
    fmt.Println("Values:", *out[0], *out[1], *out[2])
    fmt.Println("Addresses:", out[0], out[1], out[2])
}

结果:

Values: 3 3 3
Addresses: 0xc000018088 0xc000018088 0xc000018088

输出的结果都是3,意外吧!

我们加一些打印

func test3() {
    var out []*int
    for i := 0; i < 3; i++ {
        out = append(out, &i)
        fmt.Println("\t inline Values:", *out[i])
        fmt.Println("\t inline Pointers:", out[i])
    }
    fmt.Println(out)
    fmt.Println("Values:", *out[0], *out[1], *out[2])
    fmt.Println("Addresses:", out[0], out[1], out[2])
}
// 输出:
`PS D:\Go\template_learn\c03> go run .\test.go
         inline Values: 0
         inline Pointers: 0xc000018088
         inline Values: 1
         inline Pointers: 0xc000018088
         inline Values: 2
         inline Pointers: 0xc000018088
[0xc000018088 0xc000018088 0xc000018088]
Values: 3 3 3
Addresses: 0xc000018088 0xc000018088 0xc000018088`

分析一下也能知道,循环迭代器变量是一个单一的变量,在每个循环迭代中取不同的值,i始终是一个值,每次都会从底层修改当前切片中添加的元素的值,导致尽管在遍历过程中,每个值都不一样,但最后一次恒久的修改了底层的值,导致,切片中全都是这个值

解决方法

Copy i into a new variable.
把i值复制出去
这样每次都重新new一个新的,每个地址都不一样

 for i := 0; i < 3; i++ {
  i := i 
  out = append(out, &i)
 }
`PS D:\Go\template_learn\c03> go run .\test.go
         inline Values: 0
         inline Pointers: 0xc000018088
         inline Values: 1
         inline Pointers: 0xc0000180c0
         inline Values: 2
         inline Pointers: 0xc0000180c8
[0xc000018088 0xc0000180c0 0xc0000180c8]
Values: 0 1 2
Addresses: 0xc000018088 0xc0000180c0 0xc0000180c8`

问题4:循环迭代器变量使用goroutine

在 Go 中进行循环时,我们经常会使用 goroutine 来并发处理数据。最经典的就是会结合闭包来编写业务逻辑【引用自 煎鱼大佬文章】

func test4() {
    values := []int{1, 2, 3, 4, 5}
    for _, val := range values {
        go func() {
            fmt.Println(val)
        }()
    }
    // 这个必须加,不然直接退出了
    time.Sleep(time.Second)
}

结果:

5
5
5
5
5

解决方法

我在我另一篇博客很久前写过一个关于javascript的闭包
———————–链接在这里——————————————-
通过将 val 作为参数添加到闭包中,在每次循环时,变量 val 都会被存储在 goroutine 的堆栈中,以确保最终 goroutine 执行时值是对的。

当然,这里还有一个隐性问题。大家总会以为是按顺序输出 1, 2, 3, 4, 5。其实不然,因为 goroutine 的执行是具有随机性的,没法确保顺序。【引用自 煎鱼大佬文章】

func test4() {
    values := []int{1, 2, 3, 4, 5}
    for _, val := range values {
        go func(val int) {
            fmt.Println(val)
        }(val)
    }

    time.Sleep(time.Second)
}

问题5:值传递,引用传递

这个错误估计范的人很少
go中 函数都是值传递,将数组传进去,会拷贝这个数组,

func test5() {
    a := [2]int{1, 2}
    fmt.Println("init:", a)
    deal(a)
    fmt.Println("after:", a)
}
func deal(a [2]int) {
    a[0] = 3
    fmt.Println("deal:", a)
}

输出如下:

PS D:\Go\template_learn\c03> go run .\test.go
init: [1 2]
deal: [3 2]
after: [1 2]

可以发现,函数白忙活了,没改成,没打动这个数组少女的心,原因就是,函数是值传递。

解决方法

// 使用切片
PS D:\Go\template_learn\c03> go run .\test.go
init: [1 2]
deal: [3 2]
after: [1 2]

// 使用指针
func test5() {
    a := [2]int{1, 2}
    fmt.Println("init:", a)
    deal(a)
    fmt.Println("after:", a)
}
func deal(a [2]int) {
    a[0] = 3
    fmt.Println("deal:", a)
}

原因是:切片不会存储任何的数据,他的底层 data 会指向一个底层数组。因此在修改切片的元素时,会修改其底层数组的相应元素,共享同一个底层数组的其他切片会一并修改。【引用自 煎鱼大佬文章】

分类: go

浙公网安备33011302000604

辽ICP备20003309号