在Golang中使用for range語句進(jìn)行迭代非常的便捷,但在涉及到指針時(shí)就得小心一點(diǎn)了。
下面的代碼中定義了一個(gè)元素類型為*int的通道ch:
package main
import (
"fmt"
)
func main() {
ch := make(chan *int, 5)
//sender
input := []int{1,2,3,4,5}
go func(){
for _, v := range input {
ch <- &v
}
close(ch)
}()
//receiver
for v := range ch {
fmt.Println(*v)
}
}
在上面代碼中,發(fā)送方將input數(shù)組發(fā)送給ch通道,接收方再從ch通道中接收數(shù)據(jù),程序的預(yù)期輸出應(yīng)該是:
1
2
3
4
5
現(xiàn)在運(yùn)行一下程序,得到的輸出如下:
5
5
5
5
5
很明顯,程序并沒有達(dá)到預(yù)期的結(jié)果,那么問題出在哪里呢?我們將代碼稍作修改:
//receiver
for v := range ch {
fmt.Println(v)
}
得到如下輸出:
0x416020
0x416020
0x416020
0x416020
0x416020
可以看到,5次輸出變量v(*int)都指向了同一個(gè)地址,返回去檢查一下發(fā)送部分代碼:
for _, v := range input {
ch <- &v
}
問題正是出在這里,在for range語句中,v變量用于保存迭代input數(shù)組所得的值,但是v只被聲明了一次,此后都是將迭代input出的值賦值給v,v變量的內(nèi)存地址始終未變,這樣再將v的地址發(fā)送給ch通道,發(fā)送的都是同一個(gè)地址,當(dāng)然無法達(dá)到預(yù)期效果。
解決方案是,引入一個(gè)中間變量,每次迭代都重新聲明一個(gè)變量temp,賦值后再將其地址發(fā)送給ch:
for _, v := range input {
temp := v
ch <- &temp
}
抑或直接引用數(shù)據(jù)的內(nèi)存(推薦,無需開辟新的內(nèi)存空間):
for k, _ := range input {
c <- &input[k]
}
再次運(yùn)行,就可看到預(yù)期的效果。以上方案是用于討論range語句帶來的問題,當(dāng)然,平時(shí)還是盡量避免使用指針類型的通道。