函数声明
- 函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。
- 形式参数列表描述了函数的参数名以及参数类型。
- 返回值列表描述了函数返回值的变量名以及类型。
- 若函数返回一个无名变量或者没有返回值,返回值列表的括号是可以省略的。
- 若函数声明不包括返回值列表,那么函数体执行完毕后,不会返回任何值。
func name(parameter-list) (result-list) {
body
}
func add(a int ,b int) int{
return a+b
}
// 命名返回值
func min(a, b int) (min int) {
if a > b {
min = b
} else {
min = a
}
return
}
一组形参或返回值有相同的类型,不必为每个形参都写出参数类型。
func f(i, j, k int, s, t string) { /* ... */ }
func f(i int, j int, k int, s string, t string) { /* ... */ }
- 函数的类型被称为函数的签名☝️如果两个函数形式参数列表和返回值列表中的变量类型一一对应,那么这两个函数被认为有相同的类型或签名。
- 函数调用都必须按照声明顺序为所有参数提供实参。
- 实参通过值的方式传递,对形参进行修改不会影响实参。
- 实参包括引用类型,如指针,
slice
、map
等类型,实参可能会由于函数的间接引用被修改。
没有函数体的函数声明,这表示该函数不是以Go
实现的,只有函数签名。
package math
func Sin(x float64) float //implemented in assembly language
递归
函数可以是递归的,函数可以直接或间接的调用自身。
func fib(n int) int {
if n <= 1 {
return 1
}
return fib(n-1) + fib(n-2)
}
func main() {
for i := 0; i < 10; i++ {
fmt.Printf("\t%d", fib(i))
}
}
多返回值
- 在
Go
语言中,一个函数可以返回多个值。 - 标准库中的许多函数返回
2
个值,一个是期望得到的返回值,另一个是函数出错时的错误信息。
func f(a, b int) (max, min int) {
if a > b {
max = a
min = b
} else {
max = b
min = a
}
return
}
func main() {
max, min := f(10, 100)
fmt.Println(max, min)
}
http请求
package main
import (
"fmt"
"net/http"
)
func main() {
// resp 服务器响应内容
// err http请求出错时候的信息
resp, err := http.Get(`https://blog.zxysilent.com`)
if err != nil { // 不等于nil 表示有错误
panic(err) //抛出异常
}
// 准备容器
buf := make([]byte, 1024*10) //1kb*10
// 读取响应内容到 数据容器中
l, err := resp.Body.Read(buf) //读取长度和错误
fmt.Println(l, err)
// 字节转换为字符串
fmt.Println(string(buf[:l]))
}
函数值
在Go语言中,函数被看作第一类值(first-class values):函数像其他值一样,拥有类型,可以被赋值给其他变量,传递给函数,从函数返回。
func add(a, b int) int {
return a + b
}
func fn() func(int, int) int {
// 产生了一个不知名的函数
// 匿名函数
return func(a, b int) int {
return a + b
}
}
func main() {
f := add
fmt.Println(f(10, 20))
//插播内容
func() {
fmt.Println("匿名函数自执行")
}()
fmt.Println(fn()(100, 200))
}
函数类型的零值是nil
,调用值为nil
的函数值会引起panic
。
var fn func(int) int
fn(10) // 此处f的值为nil, 会引起panic错误
函数值可以与nil比较。
var fn func(int) int
if fn != nil {
fn(10)
}
✍️函数值之间是不可比较的,也不能用函数值作为map
的key
。
可变参数
- 参数数量可变的函数称为可变参数函数。
fmt.Printf
首先接收一个必备的参数,之后接收任意个数的后续参数。 - 在声明可变参数函数时,需要在参数列表的最后一个参数类型之前加上省略符号
..
,这表示该函数会接收任意数量的该类型参数。
func sum(vals...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}
sum
函数返回任意个int
型参数的和。在函数体中,vals
被看作是类型为[] int
的切片。
fmt.Println(sum())
fmt.Println(sum(3))
fmt.Println(sum(1, 2, 3, 4))
原始参数已经是切片类型,我们该如何传递给sum❓。
values := []int{1, 2, 3}
fmt.Println(sum(values...))
...int
型参数的行为看起来很像切片类型,但实可变参数函数和以切片作为参数的函数是不同的。
func f(...int) {}
func g([]int) {}
fmt.Printf("%T\n", f) // "func(...int)"
fmt.Printf("%T\n", g) // "func([]int)"
defer
函数返回前执行的函数⛲️
- 在调用普通函数或方法前加上关键字
defer
,就完成了defer
所需要的语法。 - 可以在一个函数中执行多条
defer
语句,它们的执行顺序与声明顺序相反。 - 无论包含
defer
语句的函数是通过return
正常结束,还是由于panic
导致的异常结束defer
后的函数都会被执行。 - 函数体内某个变量作为
defer
时匿名函数的参数,则在定义defer
时即已经获得了拷贝,否则则是引用某个变量的地址。
package main
import (
"fmt"
)
func main() {
//函数返回前执行
defer fmt.Println("defer1")
defer fmt.Println("defer2")
fmt.Println("exit")
}
import (
"fmt"
)
func fn() {
defer fmt.Println("异常也会执行")
fmt.Println("exit")
panic("手动异常")
}
func main() {
fn()
}
拷贝还是引用
package main
import "fmt"
func main() {
defer fmt.Println("hello defer")
// 拷贝还是引用
for i := 0; i < 5; i++ {
//普通匿名函数
func() {
fmt.Println(i)
}()
// defer 引用
defer func() {
fmt.Println("defer:", i)
}()
// defer 拷贝
defer func(x int) {
fmt.Println("defer-:", x)
}(i)
}
}
函数返回前⌛️
package main
import "fmt"
func main() {
res := test(10)
fmt.Println(res) //100
res1 := test1(10)
fmt.Println(res1) //101
}
func test(i int) int {
defer func() {
i++
}()
return i * 10
}
func test1(i int) (r int) {
defer func() {
r++
}()
r = i * 10
return
}
panic异常
Go的类型系统会在编译时捕获很多错误,但有些错误只能在运行时检查,如数组访问越界、空指针引用等。这些运行时错误会引起painc异常。
- 当panic异常发生时,程序会中断运行,并立即执行在该goroutine中被延迟的函数(
defer
)。 - 随后程序崩溃并输出日志信息。日志信息包括panic value和函数调用的堆栈跟踪信息。
-直接调用内置的panic函数也会引发
panic
异常。panic
函数接受任何值interface{}
作为参数。
recover捕获异常
当异常发生的时候程序会停止运行。当一个web
服务(教学管理系统)某一个模块出现问题(登陆)但其他模块应该可以正常提供服务(抢课)。
- 在
defer
中调用了内置函数recover
,recover
会使程序从panic
中恢复,并返回panic value
。 - 导致
panic
异常的函数不会继续运行,但能正常返回。 - 在未发生
panic
时调用recover
,recove
r会返回nil
。
package main
import "fmt"
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("defer:", err)
}
}()
panic("提前终止程序")
}
不影响其他功能
package main
import "fmt"
func fn1() {
// defer func() {
// if err := recover(); err != nil {
// fmt.Println("defer:", err)
// }
// }()
panic("panic")
}
func fn2() {
defer func() {
if err := recover(); err != nil {
fmt.Println("defer:", err)
}
}()
fmt.Println("zxysilent")
}
func main() {
for i := 0; i < 3; i++ {
fn1()
fn2()
}
}
Comments