本文参考自 脑子进煎鱼了 大佬 公众号
问题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 会指向一个底层数组。因此在修改切片的元素时,会修改其底层数组的相应元素,共享同一个底层数组的其他切片会一并修改。【引用自 煎鱼大佬文章】