设为首页 加入收藏

TOP

Go基础系列:函数(1)(三)
2018-10-30 14:08:45 】 浏览:423
Tags:基础 系列 函数
exit status 2

注意上面的end aend main都没有被输出。

可以使用recover()去捕获panic()并恢复执行。recover()用于捕捉panic()错误,并返回这个错误信息。但注意,即使recover()捕获到了panic(),但调用含有panic()函数的函数(即上面的G()函数)也会退出,所以如果recover()定义在G()中,则G()中调用F()函数之后的代码都不会执行(见下面的通用格式)。

以下是比较通用的panic()和recover()的格式:

func main() {
    G()
    // 下面的代码会执行
    ...CODE IN MAIN...
}
func G(){
    defer func (){
        if str := recover(); str != nil {
            fmt.Println(str)
        }
    }()
    ...CODE IN G()...
    
    // F()的调用必须在defer关键字之后
    F()
    // 该函数内下面的代码不会执行
    ...CODE IN G()...
}
func F() {
    ...CODE1...
    panic("error found")
    // 下面的代码不会执行
    ...CODE IN F()...
}

可以使用recover()去捕获panic()并恢复执行。但以下代码是错误的:

func main() {
    println("start main")
    a()
    println("end main")
}

func a() {
    println("start a")
    panic("panic in a")

    // 直接放在panic后是错误的
    panic_str := recover()
    println(panic_str)

    println("end a")
}

之所以错误,是因为panic()一出现就直接退出函数a()和main()了。要想recover()真正捕获panic(),需要将recover()放在defer的推迟对象中,且defer的定义必须在panic()发生之前。

例如,下面是通用格式的示例:

package main

import "fmt"

func main() {
    println("start main")
    b()
    println("end main")
}

func a() {
    println("start a")
    panic("panic in a")
    println("end a")
}

func b() {
    println("start b")
    defer func() {
        if str := recover(); str != nil {
            fmt.Println(str)
        }
    }()
    a()
    println("end b")
}

以下是输出结果:

start main
start b
start a
panic in a
end main

注意上面的end bend a都没有被输出,但是end main输出了。

panic()是内置的函数(在包builtin中),在log包中也有一个Panic()函数,它调用Print()输出信息后,再调用panic()。go doc log Panic一看便知:

$ go doc log Panic
func Panic(v ...interface{})
    Panic is equivalent to Print() followed by a call to panic().

内置函数

在builtin包中有一些内置函数,这些内置函数额外的导入包就能使用。

有以下内置函数:

$ go doc builtin | grep func
func close(c chan<- Type)
func delete(m map[Type]Type1, key Type)
func panic(v interface{})
func print(args ...Type)
func println(args ...Type)
func recover() interface{}
    func complex(r, i FloatType) ComplexType
    func imag(c ComplexType) FloatType
    func real(c ComplexType) FloatType
    func append(slice []Type, elems ...Type) []Type
    func make(t Type, size ...IntegerType) Type
    func new(Type) *Type
    func cap(v Type) int
    func copy(dst, src []Type) int
    func len(v Type) int
  • close用于关闭channel
  • delete用于删除map中的元素
  • copy用于拷贝slice
  • append用于追加slice
  • cap用于获取slice的容量
  • len用于获取
    • slice的长度
    • map的元素个数
    • array的元素个数
    • 指向array的指针时,获取array的长度
    • string的字节数
    • channel的channel buffer中的未读队列长度
  • printprintln:底层的输出函数,用来调试用。在实际程序中,应该使用fmt中的print类函数
  • compleximagreal:操作复数(虚数)
  • panicrecover:处理错误
  • newmake:分配内存并初始化
    • new适用于为值类(value type)的数据类型(如array,int等)和struct类型的对象分配内存并初始化,并返回它们的地址给变量。如v := new(int)
    • make适用于为内置的引用类的类型(如slice、map、channel等)分配内存并初始化底层数据结构,并返回它们的指针给变量,同时可能会做一些额外的操作

注意,地址和指针是不同的。地址就是数据对象在内存中的地址,指针则是占用一个机器字长(32位机器是4字节,64位机器是8字节)的数据,这个数据中存储的是它所指向数据对象的地址。

a -> AAAA
b -> Pointer -> BBBB

递归函数

函数内部调用函数自身的函数称为递归函数。

使用递归函数最重要的三点:

  1. 必须先定义函数的退出条件,退出条件基本上都使用退出点来定义,退出点常常也称为递归的基点,是递归函数的最后一次递归点,或者说没有东西可递归时就是退出点。
  2. 递归函数很可能会产生一大堆的goroutine(其它编程语言则是出现一大堆的线程、进程),也很可能会出现栈空间内存溢出问题。在其它编程语言可能只能设置最大递归深度或改写递归函数来解决这个问题,在Go中可以使用channel+goroutine设计的"lazy eva luation"来解决。
  3. 递归函数通常可以使用level级数的方式进行改写,使其不再是递归函数,这样就不会有第2点的问题。

例如,递归最常见的示例,求一个给定整数的阶乘。因为阶乘的公式为n*(n-1)*...*3*2*1,它在参数为1的时候退出函

首页 上一页 1 2 3 4 下一页 尾页 3/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇some settings for spacemacs gol.. 下一篇Go基础系列:流程控制结构

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目