用python写业务代码有三四年了,忽然切换用golang写业务的时候会遇到一些奇怪的问题。这些问题不能说golang的坑,只能说自己对于go的认知还不够清晰。
我的实际场景是这样的,我从redis取出任务后,经过各种的数据会扔给Elasticsearch,原始的数据会扔给Mysql里面。但中间遇到一个问题,我在函数里扩展了原始数据,但是在另一个函数里收到了影响。 后来通过输出对象内存地址来分析出问题的。
第一个问题是,我从main里面创建的一个slice列表,并传递给func函数,但是这函数会针对该slice切片进行修改。 一个slice从main()到func()里是一个copy地址的过程。
如果你想在func里修改slice内容,又不想让外层的slice收到影响。 可以使用copy(dst,src)。
第二个问题是,我在func里面针对slice进行append添加数据,但是在main()里看不到append的数据。 后来查询了相关的文档,我们需要使用指针地址的。
该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新。
golang slice的copy函数用法:
#xiaorui.cc n = copy(det ,src)。n等于拷贝元素个数。 array := [...]int{0,1,2,3,4,5,6,7}。 array1 := make( []int,5) // array1是dst切片 copy(array1,array[0:5])
我们通过下面的例子深究下slice append的问题,测试代码运行后得到的print内存地址是不一样的。 执行了append操作后,内存地址发生了变化,说明已经不是引用传递。
#xiaorui.cc a := []int{1, 2, 3, 4} sa := a[1:3] fmt.Printf("%p\n", sa) fmt.Println(&a[1], &sa[0]) fmt.Println(&a[1], &sb[0]) sa = append(sa, 11, 22, 33) fmt.Printf("%p\n", sa)
Slice是引用类型,里面的所有记录指向的都是内存中的同一块内存区。我们来说下Slice的处理机制,当Slice的容量还有空闲的时候,append进来的元素会直接使用空闲的容量空间,但是一旦append进来的元素个数超过了原来指定容量值的时候,内存管理器就是重新开辟一个更大的内存空间,用于存储多出来的元素,并且会将原来的元素复制一份,放到这块新开辟的内存空间。
下面是完整的测试实例.
#xiaorui.cc package main import ( "fmt" ) func modify1(s []string) { all := make([]string, 10) //如果不想改动原slice,可以先copy对象进行操作. copy(all, s[0:3]) fmt.Println(all) all[0] = "all" s[0] = "xiaorui.cc" //这里的append数据后,在main()里是不生效的. s = append(s, "modify1") fmt.Println(s) fmt.Println(all) fmt.Println("modify func end ...") } func modify2(s *[]string) { //如果要给slice append数据,需要使用指针的,不然在某函数内操作是不生效的. *s = append(*s, "this is modify2") } func main() { k := []string{"a", "b", "c"} modify1(k) fmt.Println(k) modify2(&k) fmt.Println(k) fmt.Println("last resut, k is ", k) }
下面是测试的结果.
#xiaorui.cc [ `go run g.go` | done: 402.558856ms ] [a b c ] [xiaorui.cc b c modify1] [all b c ] modify func end ... [xiaorui.cc b c] [xiaorui.cc b c this is modify2] last resut, k is [xiaorui.cc b c this is modify2] [ ~/ ] #
一般来说 string 、int … 是值传递。 array、slice、map是引用传递。 既然golang设计到这么简洁了,搞不懂为什么不针对这些细节再封装下。