【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执行过程
- 逆序执行当前 goruntine 的 defer 链(可通过 recover 介入)
- 打印错误信息和调用堆栈
- 调用 exit(2) 结束进程
recover
- recover会阻断 panic 执行
- recover 必须在defer 中才会生效