文章目录
背景
最近在学习golang编程,之前看过无闻大哥的视频,不过没有对编程中的坑进行记录。为了加深印象所以有了这边文章。
使用的go版本:go1.12.5 windows/amd64
坑一:遍历遇上指针
例子1:
type student struct {
name string
age int
}
func main() {
m := make(map[string]*student)
stus := []student{
{name: "小王子", age: 18},
{name: "娜扎", age: 23},
{name: "大王八", age: 9000},
}
for _, stu := range stus {
m[stu.name] = &stu //重点在这里----------------------------------------------
}
for k, v := range m {
fmt.Println(k, "=>", v.name)
}
}
例子2:
func main() {
slice := []int{0, 1, 2, 3}
myMap := make(map[int]*int)
for index, value := range slice {
myMap[index] = &value //重点在这里--------------------------------------------------
}
fmt.Println("=====new map=====")
prtMap(myMap)
}
func prtMap(myMap map[int]*int) {
for key, value := range myMap {
fmt.Printf("map[%v]=%v\n", key, *value)
}
}
为什么?
看坑二。
解决方案
首先他们的输出结果肯定不如人意的,那么要修正这个坑的话,要把遍历后的数据赋值给一个新的变量,最后再由新的变量赋值给map[],这样就没问题了。
例子1
for _, stu := range stus {
stu2 := stu //新增变量,并赋值
m[stu.name] = &stu2 //重点在这里----------------------------------------------
}
例子2
for index, value := range slice {
value2 := value //新增变量,并赋值
myMap[index] = &value2 //重点在这里--------------------------------------------------
}
fmt.Println("=====new map=====")
prtMap(myMap)
}
这样就可以了。
坑二:切片和闭包
使用for range来遍历切片后,赋值,其实是没问题的。哈哈~!
例子
package main
func main() {
s := []string{"a", "b", "c"} //此时s的内存地址:0xc000058420
for _, v := range s {
go func() {
println(v) //此时for读出来的值的内存地址均为:0xc00004e1c0,反向操作,通过内存地址读值所以只显示C的原因。(可能涉及到堆栈的问题,我胡乱猜测的)
}() //内存地址每台电脑都不一样的,这里是运行时产生的地址。
}
select {}
// 这里会有死锁的,但是不管,不是重点。
}
为什么
这里应该是关于指针的问题,和坑一是一样的。 如果你遍历了切片,得到切片里面的序号和元素,下一步肯定是想把序号或者元素做调用。这里涉及到内存地址的理解(我当前也不是十分了解),这里是把获取到的元素进行打印(无论何种打印,如果是引用类型的话,地址块地址肯定是一致的,按照两个例子的理解,当整个地址块被读取的时候,会取最后一个值)那么肯定就是打印最后一个值,所以这里为什么输出总是3的原因。
解决方案
package main
func main() {
s := []string{"a", "b", "c"}
for _, v := range s {
go func(v string) {
println(v)
}(v)
}
select {}
// 这里会有死锁的,但是不管,不是重点。
}
坑三:切片的append
新建一个切片88,然后向里面新加一个3元素,按道理(切片是引用类型,不够内存空间使用会双倍申请新的地址)应该输出3,但结果是加不加都是空。
例子
package main
import "fmt"
func Slice88(s []int) {
s = append(s, 3)
}
func main() {
s := make([]int, 0)
fmt.Println(s) //[]
Slice88(s)
fmt.Println(s) //[]
}
为什么
函数Slice88还没有赋值之前,Slice88和main里的s变量,内存地址都是一样的,因为s是新建的int切片,容量和个数都是0,但是经过Slice88新增一个元素后,地址就变了。切片在容量大于当前定义(s := make([]int, 0))的时候,会向内存新申请双倍的容量来扩容。那么,当前新加一个元素后,大于0,那就申请双倍的内容容量来扩容。此时,经过append后的切片地址已经不是原来的地址了。所以需要重新赋值给main函数的s变量
解决方案
将函数添加返回值,把返回值重新拷贝赋值,这样就能保证没错。
package main
import "fmt"
func Slice88(s []int) []int {
s = append(s, 3)
return s
}
func main() {
s := make([]int, 0)
fmt.Println(s)
s = Slice88(s)
fmt.Println(s)
}
坑四:time包自定义格式的坑
time包中的格式,你想自定义的话,需要用到Format这个函数。但这个函数需要注意。
例子:
package main
import (
"fmt"
"time"
)
func main() {
t := time.Now()
fmt.Println(t) //原始输出:2020-01-21 10:55:49.5006634 +0800 CST m=+0.004940201
fmt.Println(t.Format(time.ANSIC))
fmt.Println(t.Format("Mon Jan _2 15:04:05 2006")) //和上面一句是一样的。
}
为什么:
这里有个坑就是time包的Format问题,该函数里面的常量是定死了时间格式常量的,如果你不输入一模一样的时间数字和格式,那时间就会出错,如常量ANSIC:“Mon Jan _2 15:04:05 2006”
例子:fmt.Println(t.Format(“Mon Jan _2 15:04:05 2006”))
如果"Mon Jan _2 15:04:05 2006"任意输错,时间都会出错。
官方的time格式
const (
ANSIC = "Mon Jan _2 15:04:05 2006"
UnixDate = "Mon Jan _2 15:04:05 MST 2006"
RubyDate = "Mon Jan 02 15:04:05 -0700 2006"
RFC822 = "02 Jan 06 15:04 MST"
RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
RFC850 = "Monday, 02-Jan-06 15:04:05 MST"
RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"
RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone
RFC3339 = "2006-01-02T15:04:05Z07:00"
RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
Kitchen = "3:04PM"
// Handy time stamps.
Stamp = "Jan _2 15:04:05"
StampMilli = "Jan _2 15:04:05.000"
StampMicro = "Jan _2 15:04:05.000000"
StampNano = "Jan _2 15:04:05.000000000"
总结
其实归根究底都是源代码的问题,要知道底层的源码是比较复杂的,也会涉及一些算法。我作为小菜鸟没任何编程基础,数学还不合格的人来说,在这里不说太多,因为没有理解,说了只会误人子弟。自己记录即可。