前言
循環(huán)語句是一種常用的控制結(jié)構(gòu),在 Go 語言中,除了 for 關(guān)鍵字以外,還有一個 range 關(guān)鍵字,可以使用 for-range 循環(huán)迭代數(shù)組、切片、字符串、map 和 channel 這些數(shù)據(jù)類型。
但是在使用 for-range 循環(huán)迭代數(shù)組和切片的時候,是很容易出錯的,甚至很多老司機一不小心都會在這里翻車。
具體是怎么翻的呢?我們接著看。
現(xiàn)象
先來看兩段很有意思的代碼:
無限循環(huán)
如果我們在遍歷數(shù)組的同時向數(shù)組中添加元素,能否得到一個永遠都不會停止的循環(huán)呢?
比如下面這段代碼:
func main() {
arr := []int{1, 2, 3}
for _, v := range arr {
arr = append(arr, v)
}
fmt.Println(arr)
}
程序輸出:
$ go run main.go
1 2 3 1 2 3
上述代碼的輸出意味著循環(huán)只遍歷了原始切片中的三個元素,我們在遍歷切片時追加的元素并沒有增加循環(huán)的執(zhí)行次數(shù),所以循環(huán)最終還是停了下來。
相同地址
第二個例子是使用 Go 語言經(jīng)常會犯的一個錯誤。
當我們在遍歷一個數(shù)組時,如果獲取 range 返回變量的地址并保存到另一個數(shù)組或者哈希時,會遇到令人困惑的現(xiàn)象:
func main() {
arr := []int{1, 2, 3}
newArr := []*int{}
for _, v := range arr {
newArr = append(newArr, &v)
}
for _, v := range newArr {
fmt.Println(*v)
}
}
程序輸出:
$ go run main.go
3 3 3
上述代碼并沒有輸出 1 2 3,而是輸出 3 3 3。
正確的做法應(yīng)該是使用 &arr[i] 替代 &v,像這種編程中的細節(jié)是很容易出錯的。
原因
具體原因也并不復(fù)雜,一句話就能解釋。
對于數(shù)組、切片或字符串,每次迭代,for-range 語句都會將原始值的副本傳遞給迭代變量,而非原始值本身。
口說無憑,具體是不是這樣,還得靠源碼說話。
Go 編譯器會將 for-range 語句轉(zhuǎn)換成類似 C 語言的三段式循環(huán)結(jié)構(gòu),就像這樣:
// Arrange to do a loop appropriate for the type. We will produce
// for INIT ; COND ; POST {
// ITER_INIT
// INDEX = INDEX_TEMP
// VALUE = VALUE_TEMP // If there is a value
// original statements
// }
迭代數(shù)組時,是這樣:
// The loop we generate:
// len_temp := len(range)
// range_temp := range
// for index_temp = 0; index_temp < len_temp; index_temp++ {
// value_temp = range_temp[index_temp]
// index = index_temp
// value = value_temp
// original body
// }
切片:
// for_temp := range
// len_temp := len(for_temp)
// for index_temp = 0; index_temp < len_temp; index_temp++ {
// value_temp = for_temp[index_temp]
// index = index_temp
// value = value_temp
// original body
// }
從上面的代碼片段,可以總結(jié)兩點:
-
在循環(huán)開始前,會將數(shù)組或切片賦值給一個新變量,在賦值過程中就發(fā)生了拷貝,迭代的實際上是副本,這也就解釋了現(xiàn)象 1。
-
在循環(huán)過程中,會將迭代元素賦值給一個臨時變量,這又發(fā)生了拷貝。如果取地址的話,每次都是一樣的,都是臨時變量的地址。
-
參考文章:文章來源:http://www.zghlxwxcb.cn/news/detail-619233.html
garbagecollected.org/2017/02/22/…
draveness.me/golang/docs…文章來源地址http://www.zghlxwxcb.cn/news/detail-619233.html
推薦閱讀
- Go 語言教程–介紹(一)
- Go 語言教程–語言結(jié)構(gòu)(二)
- Go 語言教程–語言結(jié)構(gòu)(三)
- Go 語言教程–數(shù)據(jù)類型(四)
- Go 語言教程–語言變量(五)
- Go 語言教程–GO語言常量(六)
- Go 語言教程–GO語言運算符(七)
- Go 語言教程–GO條件和循環(huán)語句(八)
- Go 語言教程–GO語言函數(shù)(九)
- Go 語言教程–GO語言變量作用域(十)
- Go 語言教程–GO語言數(shù)組(十一)
- Go 語言教程–GO語言指針(十二)
- Go 語言教程–GO語言結(jié)構(gòu)體(十三)
- Go 語言教程–GO語言切片(Slice)(十四)
- Go 語言教程–Go 語言范圍(Range)(十五)
- Go 語言教程–Go 語言Map(集合)(十六)
- Go 語言教程–Go 語言遞歸函數(shù)(十七)
- Go 語言教程–Go 語言類型轉(zhuǎn)換(十八)
- Go 語言教程–Go 語言接口(十九)
- Go 語言教程–Go 錯誤處理(二十)
- Go 語言教程–Go 并發(fā)(二十一)
到了這里,關(guān)于【Golang】Golang進階系列教程--為什么 Go for-range 的 value 值地址每次都一樣?的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!