Skip to content

defer 题目 #43

Open
Open
@JasonJe

Description

@JasonJe
  1. deferpanic的执行顺序
package main
 
import (
    "fmt"
)
 
func main() {
    defer_call()
}

func defer_call() {
    defer func() { fmt.Println("打印前") }()
    defer func() { fmt.Println("打印中") }()
    defer func() { fmt.Println("打印后") }()
    panic("触发异常")
}

输出:

打印后
打印中
打印前
panic: 触发异常

解析:defer 的执行顺序是后进先出。当出现 panic 语句的时候,会先按照 defer 的后进先出的顺序执行,最后才会执行 panic

  1. 下面代码输出什么
func increaseA() int {
    var i int
    defer func() {
        i++
    }()
    return i
}

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

func main() {
    fmt.Println(increaseA())
    fmt.Println(increaseB())
}

解析:increaseA() 的返回参数是匿名,increaseB() 是具名。所以结果是0, 1

  1. 下面代码输出什么
func f1() (r int) {
    defer func() {
        r++
    }()
    return 0
}

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,拆解过程:
func f1() (r int) {

    // 1.赋值
    r = 0

    // 2.闭包引用,返回值被修改
    defer func() {
        r++
    }()

    // 3.空的 return
    return
}
  • f2()结果为5,拆解过程:
func f2() (r int) {
    t := 5
    // 1.赋值
    r = t

    // 2.闭包引用,但是没有修改返回值 r
    defer func() {
        t = t + 5
    }()

    // 3.空的 return
    return
}
  • f3()结果为1,拆解过程:
func f3() (r int) {

    // 1.赋值
    r = 1

    // 2.r 作为函数参数,不会修改要返回的那个 r 值
    defer func(r int) {
        r = r + 5
    }(r)

    // 3.空的 return
    return
}

2步,r 是作为函数参数使用,是一份复制,defer 语句里面的 r 和 外面的 r 其实是两个变量,里面变量的改变不会影响外层变量 r,所以不是返回 6 ,而是返回 1

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

解析:

  • 1中将 28 当做 defer 函数的参数,会把 28 缓存在栈中,等到最后执行该 defer 语句的时候取出,即输出 28

  • 2缓存的是结构体 Person{28} 的地址,最终 Person{28}age 被重新赋值为 29,所以 defer 语句最后执行的时候,依靠缓存的地址取出的 age 便是 29,即输出 29

  • 3中闭包引用,输出 29

  1. 下面代码输出什么
func hello(i int) {  
    fmt.Println(i)
}
func main() {  
    i := 5
    defer hello(i)
    i = i + 10
}

解析:hello() 函数的参数在执行 defer 语句的时候会保存一份副本在实际调用 hello() 函数时用,所以是 5

  1. return 之后的 defer 语句会执行吗,下面这段代码输出什么?
var a bool = true
func main() {
    defer func(){
        fmt.Println("1")
    }()
    if a == true {
        fmt.Println("2")
        return
    }
    defer func(){
        fmt.Println("3")
    }()
}

解析:defer 关键字后面的函数或者方法想要执行必须先注册,return 之后的defer是不能注册的, 也就不能执行后面的函数或方法。

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

输出:7

解析:第一步执行r = n + 1,接着执行第二个 defer,由于此时 f() 未定义,引发异常,随即执行第一个 defer,异常被 recover(),程序正常执行,最后 return`。

  1. 下面的代码输出什么
func F(n int) func() int {
    return func() int {
        n++
        return n
    }
}

func main() {
    f := F(5)
    defer func() {
        fmt.Println(f())
    }()
    defer fmt.Println(f())
    i := f()
    fmt.Println(i)
}

输出:7 6 8

解析:defer 后面的函数如果带参数,会优先计算参数,并将结果存储在栈中,到真正执行 defer 的时候取出。

  1. recover 的调用
func main() {
    defer func() {
        recover()
    }()
    panic(1)
}

recover()必须在 defer() 函数中直接调用才有效。

那么下面代码输出什么

func main() {
    defer func() {
        fmt.Print(recover())
    }()
    defer func() {
        defer fmt.Print(recover())
        panic(1)
    }()
    defer recover() 
    panic(2)
}

defer recover() 行代码捕获是无效的,在调用 defer() 时,便会计算函数的参数并压入栈中,所以执行 defer fmt.Print(recover()) 行代码时,此时便会捕获 panic(2)。此后的 panic(1),会被上一层的 recover() 捕获,最终输出 2 1

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