设为首页 加入收藏

TOP

Go基础系列:函数(1)(二)
2018-10-30 14:08:45 】 浏览:424
Tags:基础 系列 函数
为args的slice中,且这些参数全都是int类型。所以,在函数内部就已经有了一个args = []int{....}的数据结构。

例如,下面的例子中,min()函数要从所有参数中找出最小的值。为了实验效果,特地将前两个参数a和b独立到slice的外面。min()函数内部同时会输出保存到args中的参数值。

package main

import "fmt"

func main() {
    a,b,c,d,e,f := 10,20,30,40,50,60
    fmt.Println(min(a,b,c,d,e,f))
}

func min(a,b int,args...int) int{
    // 输出args中保存的参数
    // 等价于 args := []int{30,40,50,60}
    for index,value := range args {
        fmt.Printf("%s%d%s %d\n","args[",index,"]:",value)
    }

    // 取出a、b中较小者
    min_value := a
    if a>b {
        min_value = b
    }
    // 取出所有参数中最小值
    for _,value := range args{
        if min_value > value {
            min_value = value
        }
    }
    return min_value
}

但上面代码中调用函数时传递参数的方式显然比较笨重。如果要传递的参数过多(要比较的值很多),可以先将这些参数保存到一个slice中,再传递slice给min()函数。传递slice给函数的时候,使用SLICE...的方式即可。

func main() {
    s1 := []int{30,40,50,60,70}
    fmt.Println(min(10,20,s1...))
}

上面的赋值方式已经能说明能使用slice来理解...的行为。另外,下面的例子也能很好的解释:

func f1(s...string){
    f2(s...)
    f3(s)
}

func f2(s...string){}
func f3(s []string){}

如果各参数的类型不同,又想定义成变长参数,该如何?第一种方式,可以使用struct,第二种方式可以使用接口。接口暂且不说,如果使用struct,大概如下:

type args struct {
    arg1 string
    arg2 int
    arg3 type3
}

然后可以将args传递给函数:f(a,b int,args{}),如果args结构中需要初始化,则f(a,b int,args{arg1:"hello",arg2:22})

defer关键字

defer关键字可以让函数或语句延迟到函数语句块的最结尾时,即即将退出函数时执行,即便函数中途报错结束、即便已经panic()、即便函数已经return了,也都会执行defer所推迟的对象。

例如:

func main() {
    a()
}

func a() {
    println("in a")
    defer b()
    println("leaving a")
    //到了这里才会执行b()
}

func b() {
    println("in b")
    println("leaving b")
}

上面将输出:

in a
leaving a
in b
leaving b

即便是函数已经报错,或函数已经return返回,defer的对象也会在函数退出前的最后一刻执行。

func a() TYPE{
    ...CODE...
    
    defer b()
    
    ...CODE...
    
    // 函数执行出了错误
    
    return args
    // 函数b()都会在这里执行
}

但注意,由于Go的作用域采用的是词法作用域,defer的定义位置决定了它推迟对象能看见的变量值,而不是推迟对象被调用时所能看见的值。

例如:

package main

var x = 10
func main() {
    a()
}

func a() {
    println("start a:",x)   // 输出10
    x = 20
    defer b(x)
    x = 30
    println("leaving a:",x)  // 输出30
    // 调用defer延迟的对象b(),输出20
}

func b(x int) {
    println("start b:",x)
}

如果语句块内有多个defer,则defer的对象以LIFO(last in first out)的方式执行,也就是说,先定义的defer后执行。

func main() {
    println("start...")
    defer println("1")
    defer println("2")
    defer println("3")
    defer println("4")
    println("end...")
}

将输出:

start...
end...
4
3
2
1

defer有什么用呢?一般用来做善后操作,例如清理垃圾、释放资源,无论是否报错都执行defer对象。另一方面,defer可以让这些善后操作的语句和开始语句放在一起,无论在可读性上还是安全性上都很有改善,毕竟写完开始语句就可以直接写defer语句,永远也不会忘记关闭、善后等操作。

例如,打开文件,关闭文件的操作写在一起:

open()
defer file.Close()
... 操作文件 ...

以下是defer的一些常用场景:

  • 打开关闭文件
  • 锁定、释放锁
  • 建立连接、释放连接
  • 作为结尾输出结尾信息
  • 清理垃圾(如临时文件)

panic()和recover()

panic()用于产生错误信息并终止当前的goroutine,一般将其看作是退出panic()所在函数以及退出调用panic()所在函数的函数。例如,G()中调用F(),F()中调用panic(),则F()退出,G()也退出。

注意,defer关键字推迟的对象是函数最后调用的,即使出现了panic也会调用defer推迟的对象。

例如,下面的代码中,main()中输出一个start main之后调用a(),它会输出start a,然后就panic了,panic()会输出panic: panic in a,然后报错,终止程序。

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

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

执行结果如下:

start main
start a
panic: panic in a

goroutine 1 [running]:
main.a()
        E:/learning/err.go:14 +0x63
main.main()
        E:/learning/err.go:8 +0x4c
首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇some settings for spacemacs gol.. 下一篇Go基础系列:流程控制结构

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目