使用golang传递变量给函数时遇到的问题

用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的数据。 后来查询了相关的文档,我们需要使用指针地址的。 

该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新。

http://xiaorui.cc/2016/03/05/%E4%BD%BF%E7%94%A8golang%E4%BC%A0%E9%80%92%E5%8F%98%E9%87%8F%E7%BB%99%E5%87%BD%E6%95%B0%E6%97%B6%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98/

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


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

发表评论

邮箱地址不会被公开。 必填项已用*标注