09-Go语言函数
函数
- Go语言函数格式
- func 函数名称(形参列表)(返回值列表){函数语句}
- Go语言函数的类型
- 没有返回值没有形参的函数
func test() { fmt.Println("我是一个没返回值没形参的函数") }
- 有返回值没有形参的函数
//注意点: 当返回值只有一个的时候可以省略返回值列表的() func test2() int { return 10 }
- 没有返回值有形参的函数
//注意点: 返回值列表类型相同前面可以省略类型,直接写在后面即可 func sum() (num, value int) { num = 10 value = 20 return //这里可以不写变量默认就是返回定义的返回值变量 }
- 有返回值有形参的函数
//多个连续相同类型的形参,可以只在后面写上一个数据类型 func minus(a, b int) (value int) { value = a - b return }
- Go语言函数注意点
1.在C语言中函数定义在main函数后面,需要先声明再调用
在Go语言中可以不用声明直接使用
2.Go语言中如果函数只有一个返回值, 那么返回值列表的()可以省略
3.Go语言中如果有返回值,可以在返回值列表中定义变量,函数中可以直接使用
4.如果在返回值列表中定义了变量, 那么return后面可以不用写任何内容, 默认就会将返回值列表中定义的变量返回
5.Go语言中一个函数可以有多个返回值
6.如果一个函数有多个返回值, 但是我们只想使用其中的一个, 那么可以在接收的时候使用_
来忽略, 不需要的那个返回值
7.因为_
(下划线)在Go语言中有特殊的含义, 所以在定义标识符的时候_
(下划线)不能单独作为标识符
8.如果返回值列表中有多个返回值, 并且连续的多个返回值的数据类型都一样, 那么只用在最后一个变量
后面添加数据类型即可
9.如果形参列表中有多个形参, 并且连续的多个形参的数据类型都一样, 那么只用在最后一个变量
后面添加数据类型即可
值传递和引用传递
- 值传递
- 在C语言中基本数据类型作为函数的参数,是值传递,函数内部修改形参不会影响外面的实参值
- Go语言和C语言一样, bool float int
package main import "fmt" func main() { value := 10 fmt.Println(value) change(value) fmt.Println(value) } func change(num int) { num = 888 }
- 引用传递
- 如果想要修改外界的实参,需要函数接收指针类型
package main import "fmt" func main() { num := 20 fmt.Println(num) //20 change2(&num) fmt.Println(num) //666 } func change2(num *int) { *num = 666 }
匿名函数
-
匿名函数概念
- 普通函数都有函数名称,匿名函数就是没有名字的函数
-
使用匿名函数注意点
3.1匿名函数定义之后, 如果没有立即调用, 编译器会报错
3.2也可以将一个匿名函数保存到一个变量中
在C语言中我们知道, 我们可以定义一个指向函数的指针来保存函数的地址, 然后通过指针来调用函数
在Go语言中函数是一等公民(可以定义变量, 作为形参, 作为返回值), 所以在Go语言中也可以用函数类型来定义变量(相当于C语言中指向函数的指针)
3.3由于函数是一等公民, 所以以前定义变量所有的格式, 都可以用于定义函数类型的变量
var 变量名称 函数类型
var 变量名称 函数类型 = 匿名函数
var 变量名称 = 匿名函数
变量名称 := 匿名函数
var(
变量名称 = 匿名函数
)
//定义一个匿名函数,立即调用自己,直接在匿名函数后面加上()
func(){
fmt.Printf("我是一个匿名函数,然后立即调用")
}()
//这种方法可以查看匿名函数是什么类型的函数
f := func() {
fmt.Println("我是一个匿名函数")
}
fmt.Printf("%T\n", f)
// 定义一个函数类型的变量, 保存匿名函数
var f func() = func() {
fmt.Println("我是一个匿名函数")
}
//然后通过函数类型的变量调用匿名函数
f()
- 匿名函数应用场景
一般情况下,某个函数只调用一次,会只用匿名函数作为函数的参数,或者返回值- 匿名函数作为参数传递
package main import "fmt" func main() { //1.匿名函数在外部定义,然后传给函数 //fn将来可以保存所有接收两个int类型形参返回一个int类型结果的函数 var fn func(int, int) int = func(a, b int) int { res := a + b return res } res := calculate(10, 20, fn) fmt.Println(res) //2.匿名函数直接在函数中定义 res1 := calculate(20,20, func(a int, b int) int { value := a * b return value }) fmt.Println(res1) } func calculate(num, value int, method func(int, int) int) int { res := method(num, value) return res }
- 匿名函数作为返回值
package main import "fmt" func main() { //定义一个函数类型变量接收这个函数三种方式 //var fn func(int, int) int = test() //var fn = test() fn := test() //通过变量调用匿名函数,定义一个变量接收匿名函数的返回值 res := fn(10,20) fmt.Println(res) } //匿名函数作为函数的返回值 func test() func(int, int) int { return func(num int, value int) int { res := num + value return res } }
闭包
- 闭包的概念
- 闭包就是一个特殊的匿名函数,只要这个匿名函数使用到了外界的变量,那么这个匿名函数就叫做闭包
注意点:只要闭包还在使用外界的变量,那么这个外界的变量就会一直存在
package main import "fmt" func main() { fn := addUpper() fn() //2 fn() //3 fn() //4 } func addUpper() func() { x := 1 //2 //3 return func() { x++ fmt.Println(x) // 2 , 3 , 4 } }
- 闭包就是一个特殊的匿名函数,只要这个匿名函数使用到了外界的变量,那么这个匿名函数就叫做闭包
延迟调用
- defer语句常用于
释放资源
、解除锁定
以及错误处理
等- 例如C语言中我们申请了一块内存空间,那么不使用时我们就必须释放这块存储空间
- 例如C语言中我们打开了一个文件,那么我们不使用时就要关闭这个文件
- 例如C语言中我们打开了一个数据库, 那么我们不使用时就要关闭这个数据库
- 这一类的操作在Go语言中都可以通过defer语句来完成
//defer语句会在main函数执行完再执行 fmt.Println("申请了存储空间") defer fmt.Println("释放了存储空间") fmt.Println("使用了存储空间") fmt.Println("使用了存储空间") fmt.Println("使用了存储空间")
- 无论你在什么地方注册defer语句,它都会在所属函数执行完毕之后才会执行, 并且如果注册了多个defer语句,
那么它们会按照后进先出
的原则执行- 正是因为defer语句的这种特性, 所以在Go语言中关闭资源不用像C语言那样用完了再关闭, 我们完全可以打
开的同时就关闭, 因为无论如何defer语句都会在所属函数执行完毕之后才会执行
//多个defer语句执行顺序 defer fmt.Println("释放了存储空间1") defer fmt.Println("释放了存储空间2") defer fmt.Println("释放了存储空间3") defer fmt.Println("释放了存储空间4")
- 正是因为defer语句的这种特性, 所以在Go语言中关闭资源不用像C语言那样用完了再关闭, 我们完全可以打
init函数
- Go语言中保留了两个函数
- main函数,由系统自动调用
- init函数,也是由系统地洞调用
- 这两个函数在定义时不能有任何的参数和返回值
-
注意点
- main函数只能在main包中(package main), 并且一个程序只能有一个
- 但是init函数每个包中都可以有, 并且可以有多个(但是企业开发推荐只写一个)
- init函数的作用
- init函数用于处理当前文件的初始化操作, 在使用某个文件时的一些准备工作应该放到这里
- 多个包程序执行的调用顺序
- 加载main包
- 初始化import导入的包(初始化常量 -->初始化全局变量 --> 执行init函数 --> 执行逻辑代码)
-
初始化常量 --> 初始化全局变量 --> 执行init函数 --> 执行main函数 --> 执行逻辑代码 --> 退出程序