【Go语言】函数

一、函数的基本形式

形参与实参

  • 形参是函数内部的局部变量,实参的值会拷贝给形参
  • 在函数内部修改形参的值,不会影响到实参的值
  • 形参可以有 0个或多个
  • 参数类型相同时,可以只写一次,比如 func afgf (a,b int) {}
package main

import "fmt"

func exp(a int, b int){ // a,b 为形式参数
    n := a + b
    return n
}

func main() {
    x := 1
    y := 3
    fmt.Println(exp(x, y)) // 调用函数
}

 

 

参数传指针

如果想要通过函数修改实参的值,就需要指针类型

要注意引用类型(slice、map、channl)的指针相关用法

func exp(a, b *int) { // *int 表示指针地址解析
    *a = *a + *b
}

func main() {
    x := 1
    y := 3
    exp(&x, &y) 
    fmt.Printf("x = %d,y=%d", x, y)
}

 输出结果:

  

 

函数返回值

  • 可以返回 0个或多个参数
  • 可在 func 行直接声明要返回的变量
  • return 后面的语句不会执行
  • 无参数返回时 return 可以不写
  • 在 func 行声明了返回变量时,由于函数要求有返回值,即时已经给声明返回的变量赋值了,也需要显式写上 return
func exp(a, b int) (int, int) { // 函数定义时,声明多个返回值
    n := a + b
    m := a * b
    return n, m
}

func exp2(a, b int) {
    fmt.Println(a * b) // 无需返回值
}

func exp3(a, b int) (n int, m int) { // 函数定义时,声明函数返回的变量
    n = a + b //n 已经在函数头部声明,因此可以直接赋值
    m = a * b //n 已经在函数头部声明,因此可以直接赋值
    return    // 由于已经定义了返回变量,此处可以省略n,m
}

func main() {
    x := 1
    y := 3
    n1, m1 := exp(x, y)
    exp2(x, y)
    n2, m2 := exp3(x, y)
    fmt.Printf("n1=%d,m2=%d\n", n1, m1)
    fmt.Printf("n2=%d,m2=%d\n", n2, m2)
}

 

 

不定长参数

 不定长参数本质上是一个切片,append 函数接收的就是一个不定长参数

func get_sum(a int, other ...int) int { // other 就是一个不定长参数,实质上等于[]int
    sum := a
    for _, ele := range other {
        sum += ele
    }
    fmt.Printf("type %T, len %d, cap %d\n", other, len(other), cap(other))
    return sum
}

func main() {
    fmt.Println(get_sum(2, 3, 4, 5, 1, 1, 2))
    fmt.Println(get_sum(2, 553, 31))

 

 

 

递归函数

函数在函数内部调用自己,递归函数一定要设置中止条件否则将出现死循环

范例:

func incode(a int) {
    fmt.Println(a)
    if a < 4096 {
        a = a * 2
        incode(a)
    }
}

func main() {
    incode(13)
}

输出结果:

  

 

二、匿名函数

将函数作为数据类型使用

定义一个匿名变量赋值给sum

定义一个函数类型 foo 入参为2个int,返回 1个 int

bar 函数要求入参为 1个符合 foo 格式的函数变量f 和 1个 int a,返回结果将 入参的 int a,和 int b 作为参数 传递给入参是的函数 f

(可以将范例1 理解为 范例2)

范例1:

var sum = func(a, b int) int {  // 可以理解为 func sum(a, b int) int {}
    return a + b
}

type foo func(a, b int) int

func bar(f foo, a int) int {
    b := a * 2
    return f(a, b)
}

func main() {
    fmt.Println(bar(sum, 4))
    // bar(sum,4) => return sum(a=4,b=4*2) => 4+2
}

 输出结果:

  

 

范例2:

// 匿名函数写法
var
sum = func(a, b int) int { return a + b } type foo func(a, b int) int func bar(f foo, a int) int { b := (a - 3) * 2 return f(a, b) }
// 常规函数写法
func sum2(a, b int) int { return a + b } func bar2(a int) int { b := a * 2 return sum2(a, b) }
func main() { fmt.Println(bar(sum, 9)) // bar(sum,9) => return sum(a=9,b=(9-3)*2) => 9+12 fmt.Println(bar2(9)) }

 输出结果:

  

三、闭包

  • 闭包(Closure)是引用了自由变量的函数
  • 自由变量将和函数一同存在,即时已经离开了创造它的环境
  • 闭包复制的是原对象的指针
  • 感觉自由变量好像是全局变量,但是又被目标函数持续持有

范例1:

func down(i int) func() {
    i = i * 3
    fmt.Printf("down i = %d(%p)\n", i, &i)
    b := func() { //调用了 自由变量i 的闭包函数
        i--
        fmt.Printf("func b i=%d(%p)\n", i, &i)
    }
    return b
}

func main() {
    down_func := down(20)  // down_func => func down_func(i *int){ i--; fmt.Printf("func b i=%d(%p)\n", i, &i) }
    down_func() // down_func() => down_func(&i)
    down_func()

}

输出结果:

  

范例2:

func add(base int) func(int) int {
    return func(i int) int {
        fmt.Printf("base = %d(%p)\ti=%d\n", base, &base, i)
        base += i
        fmt.Printf("base+i = %d(%p)\n", base, &base)
        return base
    }
}

func main() {
    // down_func := down(20) // down_func => func down_func(i *int){ i--; fmt.Printf("func b i=%d(%p)\n", i, &i) }
    // down_func()           // down_func() => down_func(&i)
    // down_func()

    add_func1 := add(34)
    add_func1(5)
    add_func1(9)
    fmt.Println("===========================")
    add_func2 := add(17)
    add_func2(4)
    add_func2(7)

}

输出结果:

  

 

四、延迟调用 defer

  • defer 用于注册一个延迟调用(在函数返回之前调用)
  • defer 的典型场景是释放资源,比如关闭文件句柄,释放数据库连接等
  • 如果同一个函数里有多个 defer,则后注册的先执行
  • defer 后可以跟一个 func,defer func 如果内部发生 panic,会把 panic 暂时搁置,执行完其他 defer 后再执行这个 panic
  • defer 后不是跟 func,而直接跟一条执行语句,则相关变量在注册 defer 时就被执行了拷贝或计算

范例1:

func basic() {
    fmt.Println("AAAAA")
    defer fmt.Println("11111")
    fmt.Println("BBBBB")
    defer fmt.Println("22222")
    fmt.Println("CCCCC")
    defer fmt.Println("33333")
}

func main() {
    basic()
}

输出结果:

  

范例2:

  

func defer_exe() (i int) {
    i = 9
    defer func() {
        fmt.Printf("1\ti=%d\n", i)
    }()
    i = 10
    defer func(i int) {
        fmt.Printf("2\ti=%d\n", i)
    }(i)
    defer func() {
        fmt.Printf("3\ti=%d\n", i)
    }()
    i = 11
    defer fmt.Printf("4\ti=%d\n", i)
    i = 12
    return 5
}

func main() {
    defer_exe()
}

输出结果:

  

范例3:

func defer_panic() {
    defer fmt.Println("1111")
    n := 0
    defer func() {
        fmt.Println(2 / n)
        fmt.Println("func 2 run")
        defer fmt.Println("2222")
    }()

    defer fmt.Println("3333")

}

func main() {
    defer_panic()
    fmt.Println("main run")
}

输出结果:

  

 

五、异常处理

error

 go 语言中没有 try...catch.. 机制,所以需要在函数中返回error内容

范例:

func err_slice(arr interface{}) (int, error) {
    switch value := arr.(type) {
    case []int:
        var n int
        for _, v := range value {
            n += v
        }
        return n, nil
    default:
        return -1, errors.New("arr type is nor []int")
    }
}

func main() {
    n, err := err_slice([]float32{1, 2.2, 3, 4})
    if err == nil {
        fmt.Println(n)
    } else {
        fmt.Println(err)
    }
}

输出结果:

  

 

范例:

func err_slice(arr interface{}) (int, error) {
    switch value := arr.(type) {
    case []int:
        var n int
        for _, v := range value {
            n += v
        }
        return n, nil
    default:
        return -1, errors.New("arr type is nor []int")
    }
}

func main() {
    n, err := err_slice([]int{1, 4, 71, 4})
    if err == nil {
        fmt.Println(n)
    } else {
        fmt.Println(err)
    }
}

输出结果:

   

 

自定义error

type PathError struct {
    path string
    op string
    createTime string
    message string
}

func NewPathError(path,op,message string) PathError {
    return PathError{
        path: path,
        op: op,
        createTime: time.Now().Format("2006-01-02"),
        message: message,
    }
}

func (e PathError) Error() string{
    return e.createTime+":"+e.op+" "+e.path+" "+e.message
}

func delPath (path string) error {
    return NewPathError(path,"delete","path is not exits")
}

 

 

panic

panic触发

  • 当程序运行发生错误时会导致 panic,比如:数组越界,除0
  • 程序主动调用 panic(error)

panic执行过程

  1. 逆序执行当前 goruntine 的 defer 链(可通过 recover 介入)
  2. 打印错误信息和调用堆栈
  3. 调用 exit(2) 结束进程

 

recover

  • recover会阻断 panic 执行
  • recover 必须在defer 中才会生效

 

posted @ 2023-08-06 23:34  Janzen_Q  阅读(7)  评论(0编辑  收藏  举报