循环内临时变量问题

如题,以下代码, temp每次都会分配新地址,还是有什么规则,有官方标准吗? 自测每次都会分配新地址。 (主要牵涉到闭包捕获问题,如果每次都分配新地址,可以放心的传入闭包)

for i := 0; i < 100; i++{ temp := i fmt.Println("address of temp:%p", &temp) // call lamda(temp) }

已邀请:

h12 - https://h12.io/about

赞同来自: heml

这个问题有意思。 逃逸分析认为temp逃逸了,所以每个循环的temp都重新分配在堆上了。试试这个不逃逸版本:

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    for i := 0; i < 100; i++ {
        temp := i
        p := uintptr(unsafe.Pointer(&temp))
        fmt.Printf("address of temp:%x\n", p)
    }
}

Ky

赞同来自:

package main

func main() {
    for i := 0; i < 100; i++ {
        temp := i
        println(&temp)
    }
}

这样不会变

heml

赞同来自:

这个现象导致一下代码不保证符合预期,只能通过参数透传“i”到闭包才行? (temp是否必然是一个新变量并没有明文规定)


func main() {
    cbList := make([]func(), 5)
    for i := 0; i < 5; i++{
        temp := i
        cbList[i] = func(){
            fmt.Println("I am i:", temp)
        }
    }

    for _, v := range cbList{
        v()
    }
}

h12 - https://h12.io/about

赞同来自:

循环括号(block)内的每次循环都是一个新的scope,temp变量在每次循环体内都是不同的变量(https://golang.org/ref/spec#Blocks)。这些不同的变量是否能安全复用同一个栈上地址属于编译器优化的范畴

更有意思了,另一个例子:

尝试顺序和并发调用闭包,显然逃逸分析知道顺序的时候临时变量可以安全复用栈上的同一个地址,起goroutine的时候不能安全复用,只能分配在堆上。

package main

import (
    "fmt"
    "unsafe"    
    "sync"
)

func main() {
    wg := &sync.WaitGroup{}
    for i := 0; i < 100; i++ {
        temp := i
        wg.Add(1)
        go func() { // also try taking away "go"
            defer wg.Done()
            p := uintptr(unsafe.Pointer(&temp))
            fmt.Printf("%d %x\n", temp, p)
        }()
    }
    wg.Wait()
}

要回复问题请先登录注册