遍历修改
这是初学者最容易犯的问题,当我们试图修改切片中的数据时,直接使用for range
遍历数据并修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package main
import "fmt"
type Test struct {
ID int
Name string
}
func TestForLoop() {
var tests = []Test{
{ID: 0, Name: "0"},
{ID: 1, Name: "0"},
{ID: 2, Name: "0"},
}
for i, test := range tests {
test.Name = fmt.Sprintf("%d", i)
}
for _, test := range tests {
fmt.Println(test)
}
}
|
以上代码输出结果为:
原因
for i, test := range tests
语句中的test
是一个新定义的变量,对他的修改并不会影响到切片中原变量的值
解决方案
使用for i遍历切片:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package main
import "fmt"
type Test struct {
ID int
Name string
}
func TestForLoop() {
var tests = []Test{
{ID: 0, Name: "0"},
{ID: 1, Name: "0"},
{ID: 2, Name: "0"},
}
for i := 0; i < len(tests); i++ {
tests[i].Name = fmt.Sprintf("%d", i)
}
for _, test := range tests {
fmt.Println(test)
}
}
|
地址问题
先看代码,该代码定义了一个src切片和一个dist切片,遍历src切片并将遍历到的值地址添加到dist切片内,遍历dist切片并输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import "fmt"
func TestForLoop() {
src := []int{1, 2, 3, 4}
var dist []*int
for _, v := range src {
dist = append(dist, &v)
}
for _, v := range dist {
fmt.Println(*v)
}
}
|
输出
1
2
3
4
5
|
/Users/mulinbiao/Library/Caches/JetBrains/IntelliJIdea2023.1/tmp/GoLand/___go_build_code
4
4
4
4
|
原因
在golang的for循环中,循环内部创建的函数变量都是共享同一块内存地址,for循环总是使用同一块内存去接收循环中的的value变量的值。不管循环多少次,value的内存地址都是相同的。
解决方法
定义临时变量tmp,将v的值赋给tmp,问题就解决了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package main
import "fmt"
func TestForLoop() {
src := []int{1, 2, 3, 4}
var dist []*int
for _, v := range src {
tmp:=v
dist = append(dist, &tmp)
}
for _, v := range dist {
fmt.Println(*v)
}
}
|
变量快照
先看代码,该代码期望使用协程输出ints切片的值,但实际情况并不是。这个例子里,3个goroutine共享同一个变量i,最后输出的结果大概率是输出3 3 3。
要解决这个问题,主要有2个解决方案。
1
2
3
4
5
6
7
8
9
10
11
12
|
package main
import "fmt"
func TestForLoop() {
ints := []int{1, 2, 3}
for _, i := range ints {
go func() {
fmt.Printf("%v\n", i)
}()
}
}
|
方案一
把循环变量i作为goroutine函数的一个参数,编译器在执行go func(i int)时,就会解析到i的值,确保每个goroutine可以拿到自己想要的值。
1
2
3
4
5
6
7
8
9
10
11
12
|
package main
import "fmt"
func TestForLoop() {
ints := []int{1, 2, 3}
for _, i := range ints {
go func(i int) {
fmt.Printf("%v\n", i)
}(i)
}
}
|
方案二
创建一个新的变量,用于goroutine。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package main
import "fmt"
func TestForLoop() {
ints := []int{1, 2, 3}
for _, i := range ints {
i := i
go func() {
fmt.Printf("%v\n", i)
}()
}
}
|