函数定义和defer
defer语句
Go语言中的defer
语句会将其后面跟随的语句进行延迟处理,先被defer
的语句最后被执行,最后被defer
的语句,最先被执行。
package main
import "fmt"
func main(){
fmt.Println("start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("end")
}
defer执行的时机
在Go语言函数中return
语句在底层并不是原子操作,它分为给返回值赋值和RET指令两步。而defer
语句执行的时机就在返回值赋值操作后,RET指令执行前。
defer经典示例
package main
import "fmt"
func main(){
}
func f1(){
x := 5
defer func(){
x++ //修改的是x不是返回值
}()
return x
}
func f2(x int){
defer func(){
x++
}()
return 5 //返回值=x
}
func f3()(y int){
x := 5
defer func(){
x++ //修改的是x
}()
return x //返回值y = x = 5
}
func f4()(x int){ //函数传参修改的是副本(值传递)
defer func(x int){
x++
}(x)
return 5
}
func f5()(x int){
defer func(x *int){
(*x)++
}(&x)
return 5
}
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("1" , a , calc("10" , a , b ))
a = 0
defer calc("2" , a , calc("20" , a , b))
b = 1
}
/*
1. defer calc("1" , 1 , calc("10" , 1 , 2)) //"10" , 1 , 2 ,3
defer calc("1" , 1 , 3)
2. defer calc("2" , 0 , calc("20" , 0 , 2)) // "20" , 0 , 2 , 2
calc("2" , 0 , 2)
3. a = 0 b = 1
calc("2" , 0 ,2) // "2" , 0 , 2 ,2
4. calc("1" , 1 ,3) // "1" , 1 , 3 ,4
*/
注:
1.defer 压栈的时候必须是一个确切的状态
2.defer 压栈的时候函数参数已经确定
变量的作用域
全局变量
全局变量是定义在函数外部的变量,它在程序整个运行周期内部有效。在函数中可以访问到全局变量。
package main
import "fmt"
var num int64 = 10
//函数中查找变量的顺序
//1.先在函数内部查找
//2.找不到就在函数外部查找,一直找到全局变量
func test(){
fmt.Printf("num = %d\n" , num)
}
func main(){
test()
}
局部变量
局部变量又分两种:函数内部定义的变量无法在函数外部使用,例如下面的示例代码main函数中无法使用test函数中定义的变量x
package main
import "fmt"
func test(){
var x int64 = 10
fmt.Printf("x = %d" , x)
}
func main(){
test()
fmt.Println(x) //无法使用变量x
}
如果局部变量和全局变量重名优先使用局部变量
语句快作用域
package main
import "fmt"
for j:=0;j<5;j++{
}
fmt.Println(j) //不正确
if i:=10;i<18{
}
fmt.Println(i) //不正确
函数类型和变量
可以使用type
关键字来定一个一个函数的类型
type calculation func(int , int) int
上面的语句定义了一个calculation
类型,它是一种函数类型,这种函数接受两个int类型的参数并且返回一个int类型的返回值。
简单说,凡是满足这个条件的函数都是calculation类型的函数,例如如下add和sub都是calculation类型
package main
import "fmt"
func main(){
add()
sub()
}
func add(x,y int)int{
return x + y
}
func sub(x,y int)int{
return x - y
}
Go语言是静态类型的变量
函数作为参数的类型
package main
import "fmt"
func main(){
f1(f2)
}
func f(x func()){
}
func f2()int{
return 10
}
//有返回值的类型
func f1(x func()int){
ret := x
fmt.Println(ret)
}
函数还可以作为返回值
package main
import "fmt"
func f5(x func() int)func(int , int)int{
ret := func (a ,b int) int{
return a + b
}
return ret
}
func f2() int{
return 10
}
func main(){
f7 := f5(f2)
fmt.Printf("%T\n" , f7)
}
匿名函数和闭包
匿名函数
函数当然还可以作为返回值,但是在Go语言中函数内部不能再像之前那样定义函数了,只能定义匿名函数。匿名函数就是没有函数名的函数,定义:
func(参数)(返回值){
函数体
}
匿名函数因为没有函数名,所以没法像普通函数那样调用,所以匿名函数需要保存到某个变量或者作为立即执行函数。
package main
import "fmt"
func main(){
//将匿名函数保存到变量
add := func(x ,y int){
fmt.Println(x + y)
}
add(2 ,3)//通过变量来调用匿名函数
//自执行函数:匿名函数定义完加()直接执行
func(x , y int){
fmt.Println(x , y)
}(100 , 200)
}
匿名函数一般都用在函数内部
闭包
闭包是一个函数,这个函数包含了它外部作用域的一个变量
package main
import "fmt"
func f1(f func()){
fmt.Println(f1)
f()
}
func f2 (x ,y int){
fmt.Println("f2")
fmt.Println(x + y)
}
func f3(f func(int , int) , x , y int)func(){
tmp := func(){
f(x , y)
}
return tmp
}
func main(){
f1(f3(f2 , 10 ,20))
}
package main
import "fmt"
func addr(x int)func(int)int{
return func(y int)int{
x+=y
return x
}
}
func main(){
addr()
}
package main
import(
"fmt"
"strings"
)
func checksuffix(x string)func(string)string{
return func(name string)string{
if !strings.HasSuffix(name , x){
return name + x
}
return name
}
}
func main(){
jpg := checksuffix(".jpg")
txt := checksuffix(".txt")
fmt.Println(jpg("1"))
fmt.Println(txt("2"))
}
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
}
底层原理:
1.函数可以作为返回值
2.函数内部查找变量的顺序,先在自己内部找,找不到外层找