函数定义
func 函数名(参数) 返回值 {
函数体
}
函数的基本类型及变型
返回值、可变长参数、Go语言中函数没有默认参数这个概念
package main
import "fmt"
//函数
//函数存在的意义?
//函数是一段代码的封装
//把一段逻辑抽象出来封装到一个函数中,给他起个名字,每次用到它的时候直接用函数名调用就行了
//使用函数能够让代码结构更清晰、更简洁。
//函数的定义
func sum(x int, y int) (ret int) {
return x + y
}
//没有返回值
func f1(x int, y int) {
fmt.Println(x + y)
}
//没有参数没有返回值
func f2() {
fmt.Println("f2")
}
//没有参数但有返回值
func f3() int {
ret := 3
return ret
}
//返回值可以命名也可以不命名
//命名的返回值就相当于在函数中声明一个变量
func f4(x int, y int) (ret int) {
ret = x + y
return //使用命名返回值可以return后省略
}
//多个返回值
func f5() (int, string) {
return 1, "MoCo"
}
//参数的类型简写:当函数中连续的多个参数的类型一致时,我们可以将非最后一个参数的类型省略
func f6(x, y, z int, m, n string, i, j bool) int {
return x + y
}
//可变长参数
//可变长参数必须放在函数的最后
func f7(x string, y ...int) { //(可变长参数)...参数这写省略号表示这个y可以传多个值
fmt.Println(x)
fmt.Println(y) //y的类型是切片 []int
}
//Go语言中函数没有默认参数这个概念
func main() {
r := sum(1, 2)
fmt.Println(r)
_, n := f5()
fmt.Println(n)
f7("下雨了")
f7("下雨了", 1, 2, 3, 4, 5, 6)
}
带参数和返回值、函数参数简写、可变参数、命名返回值、调用函数、不能再声明命名函数
package main
import "fmt"
//函数:一段代码的封装
func f1() {
fmt.Println("Hello 你好!")
}
func f2(name string) {
fmt.Println("Hello", name)
}
//带参数和返回值的函数
func f3(x int, y int) int {
sum := x + y
return sum
}
//参数简写
func f4(x, y int) int {
return x + y
}
//可变参数
func f5(title string, y ...int) int {
fmt.Println(y) // y是一个int类型的切片
return 1
}
//命名返回值
func f6(x, y int) (sum int) {
sum = x + y //如果使用命名的返回值,那么在函数中可以直接使用返回值变量
return //如果使用命名的返回值,return后面可以省略返回值变量
}
//Go语言中支持多个返回值
func f7(x, y int) (sum, sub int) {
sum = x + y
sub = x - y
return
}
func main() {
f1()
f2("MoCo")
f2("Azjj")
//调用函数
fmt.Println(f3(100, 200)) //打印输出
ret := f3(100, 200)
fmt.Println(ret) //fmt.Println(f3(100, 200))
f5("MoCo", 1, 2, 3, 4, 56, 7, 22)
//在一个命名的函数中不能够再声明命名函数
// func f8(){
// }
}
作用域
package main
import (
"fmt"
)
//变量作用域
var x = 100 //定义一个全局变量
//定义一个函数
func f1() {
//x := 10
name := "Adminxe" //函数内部的作用域
//函数中查找变量的顺序
//1、先在函数内部查找
//2、找不到就往函数的外面查找,一直找到全局
fmt.Println(x, name)
}
func main() {
f1()
//fmt.Println(name) //函数内部定义的变量只能在该函数的内部使用
//语句块作用域
// if i := 10; i < 18 {
// fmt.Println("小屁孩")
// }
// fmt.Println(i)
// for j := 0; j < 5; j++ {
// fmt.Println(j)
// }
// fmt.Println(j)
}
函数类型
package main
import "fmt"
// 函数类型
func f1() {
fmt.Println("hello 你好")
}
func f2() int {
return 10
}
//函数也可以作为参数的类型
func f3(x func() int) {
ret := x()
fmt.Println(ret)
}
func f4(x, y int) int {
return x + y
}
func ff(a, b int) int {
return a + b
}
//函数还可以作为返回值
func f5(x func() int) func(int, int) int {
return ff
}
func main() {
a := f1
fmt.Printf("%T\n", a)
b := f2
fmt.Printf("%T\n", b)
f3(f2)
f3(b)
fmt.Printf("%T\n", f4)
//f3(f4)
f7 := f5(f2)
fmt.Printf("%T\n", f7)
}
匿名函数
package main
import "fmt"
//匿名函数
// var f1 = func(x, y int) {
// fmt.Println(x + y)
// }
func main() {
//函数内部没有办法声明带名字的函数,所以匿名函数用在函数内部
//匿名函数
f1 := func(x, y int) {
fmt.Println(x + y)
}
f1(10, 20)
//如果只是调用一次的函数,还可以简写成立即执行函数
func(x, y int) {
fmt.Println(x + y)
fmt.Println("Hello 你好")
}(100, 200)
}
闭包
基本概念
package main
import "fmt"
//闭包
//闭包是一个函数,这个函数包含了他外部作用域的一个变量
//闭包 = 函数 + 外部变量的引用
//底层原理
//1、函数可以作为返回值
//2、函数内部查找变量的顺序,先在自己内部找,找不到往外层找
//既能运行f2又能传到f1(第三方的包)中
func f1(f func()) {
fmt.Println("this is f1")
f()
}
func f2(x, y int) {
fmt.Println("this is f2")
fmt.Println(x + y)
}
//定义一个函数对f2包装
func f3(f func(int, int), x, y int) func() {
tmp := func() {
f(x, y)
}
return tmp
}
func main() {
//f1(f3(f2, 100, 200))
ret := f3(f2, 100, 200) //把原来需要传递两个int类型的参数包装成一个不需要传参的函数
f1(ret)
}
引申1
package main
import "fmt"
//闭包是什么
//闭包是一个函数,这个函数包含了他外部作用域的一个变量
func adder(x int) func(int) int {
return func(y int) int {
x += y
return x
}
}
func main() {
ret := adder(100)
ret2 := ret(200)
fmt.Println(ret2)
}
引申2
package main
import (
"fmt"
"strings"
)
func makeSuffixFunc(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
func main() {
jpgFunc := makeSuffixFunc(".jpg")
txtFunc := makeSuffixFunc(".txt")
fmt.Println(jpgFunc("test")) //test.jpg
fmt.Println(txtFunc("test")) //test.txt
}
引申3
package main
import "fmt"
//关键字、函数名(参数)(返回值)
func calc(base int) (func(int) int, func(int) int) {
add := func(i int) int {
base += i
return base
}
sub := func(i int) int {
base -= i
return base
}
return add, sub
}
func main() {
f1, f2 := calc(10)
fmt.Println(f1(1), f2(2)) //11 9
fmt.Println(f1(3), f2(4)) //12 8
fmt.Println(f1(5), f2(6)) //13 7
}
Defer
defer
package main
import "fmt"
//defer
//defer多用于函数结束之前释放资源(文件句柄、数据连接、socket链接)
func deferDemo() {
fmt.Println("start")
defer fmt.Println("哦哦哦") //defer 把它后面的语句延迟到函数即将返回的时候再执行
defer fmt.Println("好好好") //一个函数中可以有多个defer语句
defer fmt.Println("哈哈哈") //多个defer语句按照后进先出(先进后出)的顺序延迟执行
fmt.Println("end")
}
func main() {
deferDemo()
}
Defer案例
package main
import "fmt"
//Go语言中函数的return不是原子操作。在底层是分为两步来执行
//第一步:返回值赋值
//第二步:真正的RET返回
//函数中如果存在defer,那么defer执行的时机是第一步和第二步之间
func f1() int {
x := 5
defer func() {
x++ //修改的是x不是返回值
}()
return x
}
func f2() (x int) {
defer func() {
x++
}()
return 5
}
func f3() (y int) {
x := 5
defer func() {
x++
}()
return x
}
func f4() (x int) {
defer func(x int) {
x++
}(x)
return 5
}
func main() {
fmt.Println(f1())
fmt.Println(f2())
fmt.Println(f3())
fmt.Println(f4())
}
Defer面试题
package main
import "fmt"
//问,上面代码的输出结果是?
func calc(index string, a, b int) int {
ret := a + b
fmt.Println(index, a, b, ret)
return ret
}
func main() {
a := 1
b := 2
defer calc("AA", a, calc("A", a, b)) //只能有一层的函数调用在defer处登记,也就是说需要把其余的变量函数等的结果先算出来然后放在一遍再调用!
a = 10
defer calc("BB", a, calc("B", a, b))
b = 20
}
//代码运行流程
//1.a := 1
//2.b := 2
//3.defer calc("AA", a, calc("A", a, b))
//4.defer calc("AA", 1, calc("A", 1, 2)) //带入进去
//5.calc("A", 1, 2) // "A" 1 2 3
//6.defer calc("AA", 1, 3)
//7.a=10
//8.defer calc("BB", a, calc("B", a, b)) //带入进去
//9.defer calc("BB", 10, calc("B", 10, 2))
//10.calc("B", 10, 2)) //"B" 10 2 12
//11.defer calc("BB", 1, 12)
//12.b = 20 //没意义混淆视听的
//13.calc("BB", 1, 12) //开始执行defer "AA" 1,2,13
//14.calc("AA", 1, 3) "AA" , 1,3,4
//结果:
//"A" 1 2 3
//"B" 10 2 12
// "BB" 10,12,22
//"AA" , 1,3,4
内置函数
内置函数 | 介绍 |
close | 主要用来关闭channel |
len | 用来求长度,比如string、array、slice、map、channel |
new | 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针 |
make | 用来分配内存,主要用来分配引用类型,比如chan、map、slice |
append | 用来追加元素到数组、slice中 |
panic和recover | 用来做错误处理 |
painc和recover
package main
import "fmt"
//painc和recover
func fa() {
fmt.Println("a")
}
func fb() {
//刚刚打开数据连接
defer func() {
err := recover() //尽量不用,可以使程序遇到报错继续运行下去
fmt.Println(err)
fmt.Println("释放数据库链接……")
}()
panic("出现了严重的错误!!!") //程序崩溃退出
fmt.Println("b")
}
func fc() {
fmt.Println("c")
}
func main() {
fa()
fb()
fc()
}
注意:
- recover()必须搭配defer使用。
- defer一定要在可能引发panic的语句之前定义。
参考链接
https://www.liwenzhou.com/posts/Go/09_function/
转载请注明:Adminxe's Blog » Go语言学习–函数