golang踩坑 1.slice传参和for range赋值

简介: golang踩坑 1.slice传参和for range赋值

孤独的感觉是所有焦躁的根源。——弗洛姆《爱的艺术》


1. 前言



这篇文章我们来聊聊slice当作参数传递的时候会出现什么问题。还有for range在遍历赋值的时候会出现什么问题。


2. slice传参



package main
import "fmt"
func main() {
 slice := []int{1, 2}
 fmt.Printf("data:%v, len:%d, cap:%d\n", slice, len(slice), cap(slice))
 updateslice(slice)
 fmt.Printf("after data:%v, len:%d, cap:%d\n", slice, len(slice), cap(slice))
}
func updateslice(slice []int) {
 slice[0] = 12
 slice = append(slice, 10)
 fmt.Printf("inner data:%v, len:%d, cap:%d\n", slice, len(slice), cap(slice))
}


执行结果:


data:[1 2], len:2, cap:2
inner data:[12 2 10], len:3, cap:4
after data:[12 2], len:2, cap:2


由于切片是引用类型,所以数据修改会影响外部,实际上内外是指向同一个底层数据。但是当对updateslice函数做append操作后,发现内部的添加数据修改并没有影响到外部变量,此时可以看到内部的切片容量发生了扩容,之前的数据会被copy一份到新的地址slice,此时该扩容后的切片指向的是新的地址slice,函数内外切片并不是指向同一个底层数据。


总结:当数据调整发生扩容时,内外切片不是指向同一个底层数据;否则,指向同一个底层数组。


3. for range



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)
 }
}


对于该代码,我们的预期结果是

娜扎 => 娜扎
大王八 => 大王八
小王子 => 小王子


但是得到的结果是:

小王子 => 大王八
娜扎 => 大王八
大王八 => 大王八

原因是for遍历时,变量stu是值的副本,每次遍历仅进行struct值拷贝,它指向的地址不变,所以每一次操作m[stu.name] = &stu,实际指向的都是同一个地址,遍历完成后,存储的是结构体最后一个值的拷贝。


根本原因在于for-range会使用同一块内存去接收循环中的值。

也就是说stu指向的地址是同一个,但是值是拷贝过来的,最后大家引用的是同一个地址的值。


修改如下:

for _, stu := range stus {
 value := stu //拷贝一份就可以了
 m[stu.name] = &value
}


4. 小结



slice传递参数我们应当注意扩缩容问题,没有扩缩容,还是之前的slice,否则是新的slice。for range注意赋值问题,不要企图将for中的变量赋值给别的结构,这样会导致引用的是同一份数据。


5. 关注公众号



 微信公众号:堆栈future

相关文章
|
1月前
|
编译器 Go
Golang底层原理剖析之函数调用栈-传参和返回值
Golang底层原理剖析之函数调用栈-传参和返回值
32 0
|
1月前
|
存储 Go
Golang底层原理剖析之slice类型与扩容机制
Golang底层原理剖析之slice类型与扩容机制
33 0
|
9天前
|
Go 索引
GOLANG SLICE 的底层实现
GOLANG SLICE 的底层实现
|
1月前
|
JSON Go 数据格式
golang学习7,glang的web的restful接口结构体传参
golang学习7,glang的web的restful接口结构体传参
|
1月前
|
JSON Go 数据格式
golang学习6,glang的web的restful接口传参
golang学习6,glang的web的restful接口传参
|
1月前
|
Go
Golang深入浅出之-Go语言基础语法:变量声明与赋值
【4月更文挑战第20天】本文介绍了Go语言中变量声明与赋值的基础知识,包括使用`var`关键字和简短声明`:=`的方式,以及多变量声明与赋值。强调了变量作用域、遮蔽、初始化与零值的重要性,并提醒读者注意类型推断时的一致性。了解这些概念有助于避免常见错误,提高编程技能和面试表现。
36 0
|
8月前
|
存储 大数据 Go
100天精通Golang(基础入门篇)——第11天:深入解析Go语言中的切片(Slice)及常用函数应用
100天精通Golang(基础入门篇)——第11天:深入解析Go语言中的切片(Slice)及常用函数应用
76 0
|
1月前
|
Go
golang随笔之slice+append的陷阱
golang随笔之slice+append的陷阱
24 0
|
8月前
|
存储 编译器 Go
100天精通Golang(基础入门篇)——第4天: Go语言中的变量与常量详解:声明、赋值和使用
100天精通Golang(基础入门篇)——第4天: Go语言中的变量与常量详解:声明、赋值和使用
51 0
|
11月前
|
编译器 Go
golang slice的扩容给你整明白的
golang slice的扩容给你整明白的