用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设计到这么简洁了,搞不懂为什么不针对这些细节再封装下。
