嵌入式Linux应用开发之异常处理

嵌入式技术

1367人已加入

描述

1、defer语句

defer 语句的用途是:含有 defer 语句的函数或类型方法,会在该函数或方法将要返回之前(return之前),调用另一个函数。所以,****我们可以在函数开始时,将一些容易忘记的结束操作用defer声明好,这样保证这些结束操作能在函数结束后正确执行。当defer要调用的函数有参数时,执行 defer 语句的时候,就会对延迟函数的实参进行求值。举例如下

func printA(a int) {  

    fmt.Println("value of a in deferred function", a)
}
func main() {  
    a := 5
    defer printA(a) //此时a=5,所以实际传到printA中的参数值为5,输出为5
    a = 10
    fmt.Println("value of a before deferred function call", a)
}

当一个函数内多次调用 defer 时,Go 会把 defer 调用放入到一个栈中,随后按照后进先出(Last In First Out, LIFO)的顺序执行。

2、错误处理

2.1、error类型

在 Go 中,错误一直是很常见的。错误用内建的 error 类型来表示。就像其他的内建类型(如 int、float64 等),错误值可以存储在变量里、作为函数的返回值等等。error类型定义如下:

type error interface { //是一个接口,实现该接口的类型都可以当作错误类型
    Error() string
}

使用示例:

func main() {  
    f, err := os.Open("/test.txt")
    if err != nil {//error类型
        fmt.Println(err)
        return
    }
    fmt.Println(f.Name(), "opened successfully")//println会自动调用Error()输出错误信息
}

2.2、提取错误信息的方法

Error()返回的描述不是很详细,我们可以用下面的方法获取更详细的错误信息:

判断底层结构体类型(即实现error接口的结构体),使用结构体字段获取更多信息

判断底层结构体类型(即实现error接口的结构体),调用类型的方法获取更多信息

与error类型变量直接比较

func main() {  

    files, err := filepath.Glob("[")
    if error != nil && err == filepath.ErrBadPattern {//比较,判断是不是某个指定的错误类型
        fmt.Println(err)
        return
    }
    fmt.Println("matched files", files)
}

2.3、fmt.Errorf

fmt 包中的 Errorf 函数会根据格式说明符,规定错误的格式,并返回一个符合该错误的字符串。示例:

func circleArea(radius float64) (float64, error) {  

    if radius < 0 {
        //返回错误类型
        return 0, fmt.Errorf("Area calculation failed, radius %0.2f is less than zero", radius)
    }
    return math.Pi * radius * radius, nil
}

func main() {  
    radius := -20.0
    area, err := circleArea(radius)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Printf("Area of circle %0.2f", area)
}

3 panic和recover

有些情况,当程序发生异常时,无法继续运行。在这种情况下,我们会使用 panic 来终止程序。当函数发生 panic 时,它会终止运行,在执行完所有的defer延迟函数后,程序控制返回到该函数的调用方。这样的过程会一直持续下去,直到当前协程的所有函数都返回退出,然后程序会打印出panic信息,接着打印出堆栈跟踪(Stack Trace),最后程序终止。当程序发生 panic 时,使用 recover 可以重新获得对该程序的控制。 需要注意:

你应该尽可能地使用错误,而不是使用 panic 和 recover。只有当程序不能继续运行的时候,才应该使用 panic 和 recover 机制

**必须在defer函数中直接调用recover**。在延迟函数内调用 recover,可以取到 panic 的错误信息,并且停止panic续发事件(Panicking Sequence),程序运行恢复正常。如果在延迟函数的外部调用 recover,就不能停止panic续发事件。

只有在相同的Go协程中调用 recover 才管用。recover 不能恢复一个不同协程的 panic

使用示例

func recoverName() {  
    if r := recover(); r!= nil {//恢复程序运行
        fmt.Println("recovered from ", r)
        debug.PrintStack()//打印panic异常的堆栈信息
    }
}

func fullName(firstName *string, lastName *string) {  
    defer recoverName() //延迟函数
    if firstName == nil {
        panic("runtime error: first name cannot be nil")//触发程序异常
    }
    if lastName == nil {
        panic("runtime error: last name cannot be nil")
    }
    fmt.Printf("%s %s
", *firstName, *lastName)
    fmt.Println("returned normally from fullName")
}

func main() {  
    defer fmt.Println("deferred call in main")
    firstName := "Elon"
    fullName(&firstName, nil)
    fmt.Println("returned normally from main")
}

一个通用的recover defer使用的模板,可供参考:

func foo() (err error) {
    defer func() {
        if r := recover(); r != nil {
            switch x := r.(type) {
            case string:
                err = errors.New(x)
            case error:
                err = x
            default:
                err = fmt.Errorf("Unknown panic: %v", r)
            }
        }
    }()

    panic("TODO")
}

编辑:黄飞

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分