golang#1欄位分享給大家的循環遍歷使用小坑 ,希望對需要的朋友有所幫助!
1.for 賦值表達式; 關係式表達式或邏輯表達式; 賦值表達式{ }
for i := 0; i < 10; i++ {}
2.for 關係式或邏輯運算式{ }
n := 10for n > 0 { n--}
#3.for { }
for { fmt.Println("hello world") } // 等价于 // for true { // fmt.Println("hello world") // }
str := "abc" for i, char := range str { fmt.Printf("%d => %s\n", i, string(char)) } for i := range str { //只有一个返回值 fmt.Printf("%d\n", i) } nums := []int{1, 2, 3} for i, num := range nums { fmt.Printf("%d => %d\n", i, num) } kvs := map[string]string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s => %s\n", k, v) } for k := range kvs { //只有一个返回值 fmt.Printf("%s\n", k) } // 输出结果 // 0 => a // 1 => b // 2 => c // 0 // 1 // 2 // 0 => 1 // 1 => 2 // 2 => 3 // a => apple // b => banana // a // b
func main() { var out []*int for i := 0; i < 3; i++ { // i := i out = append(out, &i) } fmt.Println("值:", *out[0], *out[1], *out[2]) fmt.Println("地址:", out[0], out[1], out[2]) } // 输出结果 // 值: 3 3 3 // 地址: 0xc000012090 0xc000012090 0xc000012090
分析
out是一個整數指標數組變量,在for循環中,聲明了一個
i變量,每次循環將
i的位址追加到
out切片中,但每次追加的其實都是
i變量,因此我們追加的是一個相同的位址,而該位址最終的值是3。
正確做法解開程式碼中的註解
// i := i,每次迴圈時都重新建立一個新的
i變數。
func main() { a1 := []int{1, 2, 3} a2 := make([]*int, len(a1)) for i, v := range a1 { a2[i] = &v } fmt.Println("值:", *a2[0], *a2[1], *a2[2]) fmt.Println("地址:", a2[0], a2[1], a2[2]) } // 输出结果 // 值: 3 3 3 // 地址: 0xc000012090 0xc000012090 0xc000012090
分析大多數人就是在
range這裡給變數賦值的時候踩坑,因為比較隱秘,其實情況和上面的一樣,
range在遍歷值類型時,其中的
v是一個局部變量,只會聲明初始化一次,之後每次循環時重新賦值覆寫前面的,所以給
a2[i]賦值的時候其實都是同一個位址
&v,而
v最終的值為
a1最後一個元素的值,也就是3。
正確做法①
a2[i]賦值時傳遞原始指針,即
a2[i] = &a1[i] ②建立臨時變數
t := v;
a2[i] = &t③閉包(與②原理一樣),
func (v int) { a2[i] = &v }(v)
func main() { var out [][]int for _, i := range [][1]int{{1}, {2}, {3}} { out = append(out, i[:]) } fmt.Println("Values:", out)}// 输出结果// [[3] [3] [3]]
i[:]總是被本次遍歷的值所覆蓋
func main() { values := []int{1, 2, 3} wg := sync.WaitGroup{} for _, val := range values { wg.Add(1) go func() { fmt.Println(val) wg.Done() }() } wg.Wait()}// 输出结果// 3// 3// 3
分析對於主協程來講,循環是很快就跑完的,而這個時候各個協程可能才開始跑,此時
val的值已經遍歷到最後一個了,所以各協程都輸出了
3。 (如果遍歷資料龐大,主協程遍歷耗時較久的話,goroutine的輸出會根據當時候的
val的值,所以每次的輸出結果不一定相同的。)
解決方法①使用臨時變數
for _, val := range values { wg.Add(1) val := val go func() { fmt.Println(val) wg.Done() }()}
for _, val := range values { wg.Add(1) go func(val int) { fmt.Println(val) wg.Done() }(val)}
以上是記錄Go的循環遍歷使用小坑的詳細內容。更多資訊請關注PHP中文網其他相關文章!