golang进阶三:defer

732 阅读2分钟

defer 语句会延迟函数的执行直到上层函数返回。 延迟调用的参数会立刻生成,但是在上层函数返回前函数都不会被调用。

延迟的函数调用被压入一个中。当函数返回时,会按照后进先出的顺序调用被延迟的函数调用

func main() {
    defer fmt.Println("world")
    fmt.Println("hello")
}

// 先输出 hello, 再输出 world

题一

func hello(i int) {  
    fmt.Println(i)
}
func main() {  
    i := 5
    defer hello(i)
    i = i + 10
}

// 输出:5  
// hello() 函数的参数在执行 defer 语句的时候会保存一份副本

题二

f1, f2, f3 各返回什么

func f1() (r int) {
    defer func() {
        r++
    }()
    return r
}

func f2() (r int) {
    t := 5
    defer func() {
        t = t + 5
    }()
    return t
}

func f3() (r int) {
    defer func(r int) {
        r = r + 5
    }(r)
    return 1
}

// f1() 返回 1
// 按三部拆开:
// r初始化: r = 0;
// func: r++: r = 1;
// 再返回 return r

// f2() 返回 5
// 按三部拆开:r = 0, t = 5; r = t; func: t = t + 5; return r

// f3() 返回 1
// 按三部拆开:r = 0; r = 1; func:r=r+5; return

题三

func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}
// 输出: 3210

type Person struct {
    age int
}
func main() {
    person := &Person{28}
    // #1. 
    defer fmt.Println(person.age)
    // #2.
    defer func(p *Person) {
        fmt.Println(p.age)
    }(person)  
    // #3.
    defer func() {
        fmt.Println(person.age)
    }()
    person.age = 29 // #A
}
// 输出 29 29 28
// 输出顺序:#3 #2 #1.
// #1 存的28, #2 存的指针,后面修改为29,调用时29 #3 存了一个闭包,调用时29

// 如果把 #A 处改成:person = &Person{29}
// 那么结果为:29 28 28

题四

func calc(index string, a, b int) int {
    ret := a + b
    fmt.Println(index, a, b, ret)
    return ret
}

func main() {
    a := 1
    b := 2
    defer calc("1", a, calc("10", a, b))
    a = 0
    defer calc("2", a, calc("20", a, b))
    b = 1
}
// 输出:
// 10 1 2 3
// 20 0 2 2
// 2 0 2 2
// 1 1 3 4

题五

func f(n int) (r int) {
    defer func() {
        r += n
        recover()
    }()

    var f func()

    defer f()
    f = func() {
        r += 2
    }
    return n + 1
}

func main() {
    fmt.Println(f(3))
}
// 还是按三部拆开
1. r = n + 1 (即 r = 4)
2. 接着执行第二个 defer,由于此时 f() 未定义,引发异常
   执行第一个 defer,异常被 recover(),程序正常执行 r += n (即 r = 7)
3. return