问题 | golang编程中的坑


背景

最近在学习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"

总结

其实归根究底都是源代码的问题,要知道底层的源码是比较复杂的,也会涉及一些算法。我作为小菜鸟没任何编程基础,数学还不合格的人来说,在这里不说太多,因为没有理解,说了只会误人子弟。自己记录即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值