函数
一、什么是函数
- 函数是基本的代码块,用于执行一个任务。
- Go 语言最少有个 main( ) 函数。
- 可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务。
- 函数声明告诉了编译器函数的名称,返回类型,和参数。
1.函数的声明
Go 语言函数定义格式如下:
func functionName( [parameter list] ) [returnType] {
函数体
}
package main
import "fmt"
/*
-- 函数是基本的代码块,用于执行一个任务
-- Go 语言最少有个 main() 函数
-- 可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务
-- 函数声明告诉了编译器函数的名称,返回类型和参数
*/
func main() {
fmt.Println(add(3, 4))
}
// func 函数名 (参数,参数....) 函数调用后的返回值 {
// 函数体 : 执行一段代码
// return 返回结果
//}
func add(a, b int) int {
c := a + b
return c
}
输出
二、函数的声明和调用
- 无参数无返回值函数
- 有一个参数的函数
- 有两个参数的函数
- 有一个返回值的函数
- 有多个返回值的函数
package main
import "fmt"
func main() {
c := add(3, 4)
fmt.Println(c)
onePrint("guan")
x, y := swap("guan", "hello")
fmt.Println(x, y)
}
// - 无参数无返回值函数
func Println() {
fmt.Println("printinfo")
}
// - 有一个参数的函数
func onePrint(name string) {
fmt.Println("name")
}
// - 有两个参数的函数
// - 有一个返回值的函数
func add(a, b int) int {
c := a + b
return c
}
// - 有多个返回值的函数
func swap(x, y string) (string, string) {
return y, x
}
输出
三、形参和实参
形式参数:定义函数时,用来接收外埠传入数据的参数,就是形式参数
实际参数:调用函数时,传给形参的实际数据叫做实际参数
package main
func main() {
maxNum := max(6, 8)
println(maxNum)
}
// - max 两个数字比大小
// 形式参数:定义函数时,用来接收外埠传入数据的参数,就是形式参数
// 实际参数:调用函数时,传给形参的实际数据叫做实际参数
func max(num1, num2 int) int {
var result int
if num1 > num2 {
result = num1
} else {
result = num2
}
// 一个函数定义上有返回值,那么函数中必须使用return语句
// 返回值
// 调用处需要使用变量接收结果
return result
}
四、可变参数
概念:一个函数的参数类型确定,但个数不确定,就可以使用可变参数。
func myfunc(arg ... int) {}
// arg ... int 告诉go这个函数接收不定数量的参数,类型全部是int
package main
import "fmt"
func main() {
getSum(1, 2, 3, 4, 5, 6, 7, 8)
}
// ... 可变参数
func getSum(nums ...int) {
sum := 0
// x.len(),获取元素长度
for i := 0; i < len(nums); i++ {
// x[i] 通过下标取出数据
sum += nums[i]
}
fmt.Println("总和:", sum)
}
输出
五、值传递和引用传递
按照数据的存储特点来分:
- 值类型的数据:操作的是数据木身、int、string、bool、 float64、array…
- 引川类型的数据:操作的是数据的地址 slice,map, chan…
值传递
package main
import "fmt"
func main() {
// 定义一个数组
arr1 := [4]int{1, 2, 3, 4}
fmt.Println("arr1默认数据", arr1)
update(arr1)
fmt.Println("arr1 调用函数后数据", arr1)
// arr2 的数据是 从arri 复制兴的,所以是不可的空间
// 修改 arr2 并不会影响 arr 1
// 值传递:传递的是数据的副本,修改数据,对于原始约数据没有影响
// 值类型的数据,默认都是值传递:基础炎型、array、struct
}
func update(arr2 [4]int) {
fmt.Println("arr2 接以数据", arr2)
arr2[0] = 10
fmt.Println("arr2 修改后数据", arr2)
}
输出
注意事项:
- 如果一个函数的参数是可变参数,同时还有其他的参数,可变参数要放在列表的最后。
- 一个函数的参数列表中最多只能有一个可变参数。
六、函数中变量的作用域
- 作用域:变量可以使用的范围
- 局部变量:函数内部定义的变量,叫做局部变量
package main
import "fmt"
func main() {
// 函数体内的局部变量
y := 66
if z := 1; z <= 10 {
// 语句内的局部变量
y := 88
fmt.Println(y) // 局部变量,就近原则
fmt.Println(z)
}
fmt.Println(y)
//fmt.Println(z) // # command-line-arguments
// .\hello.go:15:14: undefined: z
}
func gun01() {
// x 为局部变量
x := 1
println(x)
}
func gun02() {
// fmt.Println(a) //不能在使用在其他函数定义的变量
}
- 全部变量:函数外部定义的变量,叫做全局变量
package main
import "fmt"
// 全局变量
var num int = 99
func main() {
//num := 1
fmt.Println(num)
gun01()
gun02()
}
func gun01() {
//num := 2
println(num)
}
func gun02() {
//num := 3
fmt.Println(num)
}
输出
七、defer
defer 语义:推迟、延迟
在Go语言中,使用 defer 关键字来延迟一个函数或者方法的执行。
package main
import "fmt"
func main() {
num := 1
fmt.Println(num)
defer gun01() // 会被延迟到最后执行
gun02()
}
func gun01() {
num := 2
println(num)
}
func gun02() {
num := 3
fmt.Println(num)
}
输出
defer函数或者方法:个函数或方法的执行被延迟了
- 可以在函数中添加多个defer语句,
当函数执行到最后时,这些defer语句会按照逆序执行,最后该函数返回
,特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题 - 如果有很多调用 defer,那么 defer 是采用 后讲先出(栈)模式。
package main
import "fmt"
func main() {
num := 1
num01 := 6
fmt.Println(num)
defer gun01() // 会被延迟到最后执行
defer gun02()
defer fmt.Println(num01)
}
func gun01() {
num := 2
println(num)
}
func gun02() {
num := 3
fmt.Println(num)
}
输出
import "fmt"
func main() {
num := 1
a := 6
fmt.Println(num)
defer gun01(a) // 在执行a++之前,参数就已经传递进去了,在最后执行
a++
fmt.Println(a)
}
func gun01(x int) {
println(x)
}
输出
八、函数的本质探究
package main
import "fmt"
// func() 本身就是一个数据类型
func main() {
// f1 如果不加括号,函数也是一个变量
// f1() 如果加了括号,那就成了函数的调用
fmt.Print("%T\n", f1)
// f1 看着对应函数名对应函数体的地址 0xbbfd60
// f1() 带括号就是将函数直接调用
// 直接定义一个函数类型的变量
fmt.Println()
var x func(int, int)
fmt.Println(x)
// 将格式相同的f1给x进行函数赋值
x = f1
fmt.Println(x) // 和f1地址相同
f1(1, 2)
x(10, 20) //x也是函数类型的,可以直接调用
}
func f1(a, b int) {
fmt.Printf("a:%d,b:%d\n", a, b)
}
函数在 Go 语言中是复合类型,可以看做是一种特殊的变量。
函数名( ): 调用返回结果
函数名:指向函数体的内存地址,一种特殊类型的指针变量
九、匿名函数
package main
import "fmt"
// func() 本身就是一个数据类型
func main() {
f1()
f2 := f1 //函数本身也是一个变量
f2()
// 匿名函数
f3 := func() {
fmt.Println("f3函数")
}
f3()
func() {
fmt.Println("f4函数")
}() // 匿名函数本身可以调用自己
func(a, b int) {
fmt.Println(a, b)
fmt.Println("f5函数")
}(3, 4) // 匿名函数本身可以调用自己
x := func(a, b int) int {
fmt.Println("f6函数")
return a + b
}(3, 4) // 匿名函数本身可以调用自己
fmt.Println(x)
}
func f1() {
fmt.Println("f1函数")
}
Go 语言是支持函数式编程:
1、将匿名函数作为另一个函数的参数,回调函数
2、将匿名函数作为另外一个函数的返回值,可以形成闭包结构
十、回调函数
根据Go语言的数据类型的特点,可以将一个函数作为另外一个函数的参数
。
fun1()、fun2() 将 fun1 函数作为 fun2 这个函数的参数
fun2函数:叫做高阶函数,接收一个函数作为参数的函数
fun1函数:叫做回调函数,作为另外一个函数的参数
package main
import "fmt"
func main() {
r1 := Sum(1, 1)
fmt.Println(r1)
r2 := oper(5, 5, Sum)
fmt.Println(r2)
r3 := oper(10, 5, Sub)
fmt.Println(r3)
r4 := oper(10, 5, func(a int, b int) int {
if b == 0 {
fmt.Println("除数不能为0")
return 0
}
return a / b
})
fmt.Println(r4)
}
// 高阶函数,可以接收一个函数作为参数
func oper(a, b int, f func(int, int) int) int {
r := f(a, b)
return r
}
func Sum(a, b int) int {
return a + b
}
func Sub(a, b int) int {
return a - b
}
输出
十一、闭包
-
一个外层函数中,有人层函数,该人层函数中,会操作外层函数的局部变量廿月该外层函数约返回传就是这个人层函数。这个内层羽数和外层尿数的灵部变量,统称为闭包结构
-
局部变量约牛命周期就会发生改变,正常的局部变量会随着函数的调月而创建,随着函数的结束而销毁作是闭勺结构中约外票列数的局部变量并不会随着外层函数的计束而销毁,因为内票函数证什继续使用
package main
import "fmt"
func main() {
r1 := increment()
fmt.Println(r1)
v1 := r1()
fmt.Println(v1)
v2 := r1()
fmt.Println(v2)
fmt.Println(r1())
fmt.Println(r1())
fmt.Println(r1())
r2 := increment()
v3 := r2()
fmt.Println(v3)
fmt.Println(r1())
fmt.Println(r2())
}
func increment() func() int {
// 局部变量i
i := 0
// 定义一个匿名函数,给变量自增并返回
f := func() int {
i++
return i
}
return f
}