Skip to content

for ...range 题目 #44

Open
Open
@JasonJe

Description

@JasonJe
  1. 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)
    }
}
  1. 下面这段代码输出什么
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 的输出是无序的。

  1. 下面这段代码能否正常结束
func main() {
    v := []int{1, 2, 3}
    for i := range v {
        v = append(v, i)
    }
}

解析:不会出现死循环,能正常结束。循环次数在循环开始前就已经确定,循环内改变切片的长度,不影响循环次数。

  1. 下面这段代码输出什么
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 使用短变量声明 (:=) 的形式迭代变量,需要注意的是,变量 iv 在每次循环体中都会被重用,而不是重新声明。

各个 goroutine 中输出的 i、v 值都是 for range 循环结束后的 iv 最终值,而不是各个 goroutine 启动时的iv值。可以理解为闭包引用,使用的是上下文环境的值。

两种可行的修正方法:

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)
    }()
}
  1. 下面这段代码输出什么
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]
  1. 下面这段代码输出什么
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 可以获得修改后的数组元素。

  1. 下面的代码输出什么
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 使用短变量声明(:=)的形式迭代变量时,变量 ivalue 在每次循环体中都会被重用,而不是重新声明。所以 s2 每次填充的都是临时变量 value 的地址,而在最后一次循环中,value 被赋值为{c}。因此,s2输出的时候显示出了三个&{c}`。

  1. 下面代码里的 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)
}

输出:23

解析:for range map是无序的。

  1. 下面代码输出什么
func main() {
    x := []string{"a", "b", "c"}
    for v := range x {
        fmt.Print(v)
    }
}

输出:0 1 2

  1. 下面代码输出什么
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

Metadata

Metadata

Assignees

No one assigned

    Labels

    GoGolang

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions