Toggle navigation
主页
English
K8S
Golang
Guitar
About Me
归档
标签
Welcome to Sanger's Blog!
函数
go
2024-01-10 17:54:46
14
0
0
sanger
go
# 函数 ## 函数的基本形式 ```Go //函数定义。a,b是形参 func argf(a int, b int) { a = a + b } var x, y int = 3, 6 argf(x, y) //函数调用。x,y是实参 ``` - 形参是函数内部的局部变量,实参的值会拷贝给形参。 - 函数定义时的第一个的大括号不能另起一行。 - 形参可以有0个或多个。 - 参数类型相同时可以只写一次,比如argf(a,b int)。 - 在函数内部修改形参的值,实参的值不受影响。 - 如果想通过函数修改实参,就需要指针类型。 ```Go func argf(a, b *int) { *a = *a + *b *b = 888 } var x, y int = 3, 6 argf(&x, &y) ``` slice、map、channel都是引用类型,它们作为函数参数时其实跟普通struct没什么区别,都是对struct内部的各个字段做一次拷贝传到函数内部。 ```Go package main import "fmt" func slice_arg_1(arr []int) { //slice作为参数,实际上是把slice的arrayPointer、len、cap拷贝了一份传进来 arr[0] = 1 //修改底层数据里的首元素 arr = append(arr, 1) //arr的len和cap发生了变化,不会影响实参 } func main() { arr := []int{8} slice_arg_1(arr) fmt.Println(arr[0]) //1 fmt.Println(len(arr)) //1 } ``` 关于函数返回值 - 可以返回0个或多个参数。 - 可以在func行直接声明要返回的变量。 - return后面的语句不会执行。 - 无返回参数时return可以不写。 ```Go func returnf(a, b int) (c int) { //返回变量c已经声明好了 a = a + b c = a //直接使用c return //由于函数要求有返回值,即使给c赋过值了,也需要显式写return } ``` 不定长参数实际上是slice类型。 ```Go func variable_ength_arg(a int, other ...int) int { sum := a for _, ele := range other {//不定长参数实际上是slice类型 sum += ele } fmt.Printf("len %d cap %d\n", len(other), cap(other)) return sum } variable_ength_arg(1) variable_ength_arg(1,2,3,4) ``` append函数接收的就是不定长参数。 ```Go arr = append(arr, 1, 2, 3) arr = append(arr, 7) arr = append(arr) slice := append([]byte("hello "), "world"...) //...自动把"world"转成byte切片,等价于[]byte("world")... slice2 := append([]rune("hello "), []rune("world")...) //需要显式把"world"转成rune切片 ``` 在很多场景下string都隐式的转换成了byte切片,而非rune切片,比如"a中"[1]是228而非"中"。 递归函数 ```Go func Fibonacci(n int) int { if n == 0 || n == 1 { return n //凡是递归,一定要有终止条件,否则会进入无限循环 } return Fibonacci(n-1) + Fibonacci(n-2) //递归调用函数自身 } ``` ## 匿名函数 函数也是一种数据类型。 ```Go func function_arg1(f func(a, b int) int, b int) int { //f参数是一种函数类型 a := 2 * b return f(a, b) } type foo func(a, b int) int //foo是一种函数类型 func function_arg2(f foo, b int) int { //参数类型看上去简洁多了 a := 2 * b return f(a, b) } type User struct { Name string bye foo //bye的类型是foo,而foo代表一种函数类型 hello func(name string) string //使用匿名函数来声明struct字段的类型 } ch := make(chan func(string) string, 10) ch <- func(name string) string { //使用匿名函数 return "hello " + name } ``` ## 闭包 闭包(Closure)是引用了自由变量的函数,自由变量将和函数一同存在,即使已经离开了创造它的环境。闭包复制的是原对象的指针。 ```Go package main import "fmt" //闭包(Closure)是引用了自由变量的函数。自由变量将和函数一同存在,即使已经离开了创造它的环境。 func sub() func() { i := 10 fmt.Printf("%p\n", &i) b := func() { fmt.Printf("i addr %p\n", &i) //闭包复制的是原对象的指针 i-- //b函数内部引用了变量i fmt.Println(i) } return b //返回了b函数,变量i和b函数将一起存在,即使已经离开函数sub() } // 外部引用函数参数局部变量 func add(base int) func(int) int { return func(i int) int { fmt.Printf("base addr %p\n", &base) base += i return base } } func main() { b := sub() b() b() fmt.Println() tmp1 := add(10) fmt.Println(tmp1(1), tmp1(2)) //11,13 // 此时tmp1和tmp2不是一个实体了 tmp2 := add(100) fmt.Println(tmp2(1), tmp2(2)) //101,103 } ``` ## 延迟调用defer - defer用于注册一个延迟调用(在函数返回之前调用)。 - defer典型的应用场景是释放资源,比如关闭文件句柄,释放数据库连接等。 - 如果同一个函数里有多个defer,则后注册的先执行。 - defer后可以跟一个func,func内部如果发生panic,会把panic暂时搁置,当把其他defer执行完之后再来执行这个。 - defer后不是跟func,而直接跟一条执行语句,则相关变量在注册defer时被拷贝或计算。 ```Go func basic() { fmt.Println("A") defer fmt.Println(1) fmt.Println("B") //如果同一个函数里有多个defer,则后注册的先执行 defer fmt.Println(2) fmt.Println("C") } ``` ```Go func defer_exe_time() (i int) { i = 9 defer func() { //defer后可以跟一个func fmt.Printf("first i=%d\n", i) //打印5,而非9。充分理解“defer在函数返回前执行”的含义,不是在“return语句前执行defer” }() defer func(i int) { fmt.Printf("second i=%d\n", i) //打印9 }(i) defer fmt.Printf("third i=%d\n", i) //defer后不是跟func,而直接跟一条执行语句,则相关变量在注册defer时被拷贝或计算 return 5 } ``` ## 异常处理 go语言没有try catch,它提倡返回error。 ```Go func divide(a, b int) (int, error) { if b == 0 { return -1, errors.New("divide by zero") } return a / b, nil } if res, err := divide(3, 0); err != nil {//函数调用方判断error是否为nil fmt.Println(err.Error()) } ``` Go语言定义了error这个接口,自定义的error要实现Error()方法。 ```Go type PathError struct { //自定义error path string op string createTime string message string } func (err PathError) Error() string { //error接口要求实现Error() string方法 return err.createTime + ": " + err.op + " " + err.path + " " + err.message } ``` 何时会发生panic: - 运行时错误会导致panic,比如数组越界、除0。 - 程序主动调用panic(error)。 panic会执行什么: 1. 逆序执行当前goroutine的defer链(recover从这里介入)。 2. 打印错误信息和调用堆栈。 3. 调用exit(2)结束整个进程。 ```Go func soo() { fmt.Println("enter soo") defer func() { //去掉这个defer试试,看看panic的流程。把这个defer放到soo函数末尾试试 //recover必须在defer中才能生效 if err := recover(); err != nil { fmt.Printf("soo函数中发生了panic:%s\n", err) } }() fmt.Println("regist recover") defer fmt.Println("hello") defer func() { n := 0 _ = 3 / n //除0异常,发生panic,下一行的defer没有注册成功 defer fmt.Println("how are you") }() } ```
上一篇:
结构体
下一篇:
面向接口编程
0
赞
14 人读过
新浪微博
微信
更多分享
腾讯微博
QQ空间
人人网
文档导航