Description
for range
和数组的取值方式
func main() {
slice := []int{0,1,2,3}
m := make(map[int]*int)
for key,val := range slice {
m[key] = &val
}
for k,v := range m {
fmt.Println(k,"->",*v)
}
}
输出:
0 -> 3
1 -> 3
2 -> 3
3 -> 3
解析:for range
循环时候,会创建每个元素的副本,而不是每个元素的引用,所以m[key] = &val
取得的是变量val
的地址,所以最后map
中的所有元素的值都是变量val
的地址,因为最后val
被赋值为3
,所以输出都是3
。
正确的写法:
func main() {
slice := []int{0,1,2,3}
m := make(map[int]*int)
for key,val := range slice {
value := val
m[key] = &value
}
for k,v := range m {
fmt.Println(k,"===>",*v)
}
}
- 下面这段代码输出什么
func main() {
m := map[int]string{0:"zero",1:"one"}
for k,v := range m {
fmt.Println(k,v)
}
}
输出:
0 zero
1 one
// 或者
1 one
0 zero
解析:map
的输出是无序的。
- 下面这段代码能否正常结束
func main() {
v := []int{1, 2, 3}
for i := range v {
v = append(v, i)
}
}
解析:不会出现死循环,能正常结束。循环次数在循环开始前就已经确定,循环内改变切片的长度,不影响循环次数。
- 下面这段代码输出什么
func main() {
var m = [...]int{1, 2, 3}
for i, v := range m {
go func() {
fmt.Println(i, v)
}()
}
time.Sleep(time.Second * 3)
}
输出:
2 3
2 3
2 3
解析:for range
使用短变量声明 (:=)
的形式迭代变量,需要注意的是,变量 i
、v
在每次循环体中都会被重用,而不是重新声明。
各个 goroutine
中输出的 i、v 值都是 for range
循环结束后的 i
、v
最终值,而不是各个 goroutine
启动时的i
、v
值。可以理解为闭包引用,使用的是上下文环境的值。
两种可行的修正方法:
a. 使用函数传递
for i, v := range m {
go func(i,v int) {
fmt.Println(i, v)
}(i,v)
}
b. 使用临时变量保留当前值
for i, v := range m {
i := i // 这里的 := 会重新声明变量,而不是重用
v := v
go func() {
fmt.Println(i, v)
}()
}
- 下面这段代码输出什么
func main() {
var a = [5]int{1, 2, 3, 4, 5}
var r [5]int
for i, v := range a {
if i == 0 {
a[1] = 12
a[2] = 13
}
r[i] = v
}
fmt.Println("r = ", r)
fmt.Println("a = ", a)
}
输出:
r = [1 2 3 4 5]
a = [1 12 13 4 5]
解析:range
表达式是副本参与循环,就是说例子中参与循环的是 a
的副本,而不是真正的 a
。
修复写法:
func main() {
var a = [5]int{1, 2, 3, 4, 5}
var r [5]int
for i, v := range &a {
if i == 0 {
a[1] = 12
a[2] = 13
}
r[i] = v
}
fmt.Println("r = ", r)
fmt.Println("a = ", a)
}
输出:
r = [1 12 13 4 5]
a = [1 12 13 4 5]
- 下面这段代码输出什么
func main() {
var a = []int{1, 2, 3, 4, 5}
var r [5]int
for i, v := range a {
if i == 0 {
a[1] = 12
a[2] = 13
}
r[i] = v
}
fmt.Println("r = ", r)
fmt.Println("a = ", a)
}
输出:
r = [1 12 13 4 5]
a = [1 12 13 4 5]
解析:这的 a
是一个切片,切片在 Go
的内部结构有一个指向底层数组的指针,当 range
表达式发生复制时,副本的指针依旧指向原底层数组,所以对切片的修改都会反应到底层数组上,所以通过 v
可以获得修改后的数组元素。
- 下面的代码输出什么
type Foo struct {
bar string
}
func main() {
s1 := []Foo{
{"A"},
{"B"},
{"C"},
}
s2 := make([]*Foo, len(s1))
for i, value := range s1 {
s2[i] = &value
}
fmt.Println(s1[0], s1[1], s1[2])
fmt.Println(s2[0], s2[1], s2[2])
}
输出:
{A} {B} {C}
&{C} &{C} &{C}
解析:for range
使用短变量声明(:=
)的形式迭代变量时,变量 i
、value
在每次循环体中都会被重用,而不是重新声明。所以 s2
每次填充的都是临时变量 value
的地址,而在最后一次循环中,value 被赋值为
{c}。因此,
s2输出的时候显示出了三个
&{c}`。
- 下面代码里的
counter
的输出值
func main() {
var m = map[string]int{
"A": 21,
"B": 22,
"C": 23,
}
counter := 0
for k, v := range m {
if counter == 0 {
delete(m, "A")
}
counter++
fmt.Println(k, v)
}
fmt.Println("counter is ", counter)
}
输出:2
或3
解析:for range map
是无序的。
- 下面代码输出什么
func main() {
x := []string{"a", "b", "c"}
for v := range x {
fmt.Print(v)
}
}
输出:0 1 2
- 下面代码输出什么
func main() {
var k = 9
for k = range []int{} {}
fmt.Println(k)
for k = 0; k < 3; k++ {
}
fmt.Println(k)
for k = range (*[3]int)(nil) {
}
fmt.Println(k)
}
输出:9 3 2