Go语言之函数补充defer语句,递归函数,章节练习

defer语句是go语言提供的一种用于注册延迟调用的机制,是go语言中一种很有用的特性。

defer语句注册了一个函数调用,这个调用会延迟到defer语句所在的函数执行完毕后执行,所谓执行完毕是指该函数执行了return语句、函数体已执行完最后一条语句或函数所在协程发生了恐慌。

go 复制代码
fmt.Println("test01")
defer fmt.Println("test02")
fmt.Println("test03")

编程经常会需要申请一些资源,比如数据库连接、打开文件句柄、申请锁、获取可用网络连接、申请内存空间等,这些资源都有一个共同点那就是在我们使用完之后都需要将其释放掉,否则会造成内存泄漏或死锁等其它问题。但操作完资源忘记关闭释放是正常的,而defer可以很好解决这个问题

go 复制代码
// 打开文件
file_obj,err:=os.Open("满江红")
if err != nil {
    fmt.Println("文件打开失败,错误原因:",err)
}
// 关闭文件
defer file_obj.Close()
// 操作文件

多个defer执行顺序

当一个函数中有多个defer语句时,会按defer定义的顺序逆序执行,也就是说最先注册的defer函数调用最后执行。

go 复制代码
fmt.Println("test01")
defer fmt.Println("test02")
fmt.Println("test03")
defer fmt.Println("test04")
fmt.Println("test05")

defer的拷贝机制

go 复制代码
// 案例1
foo := func() {
fmt.Println("I am function foo1")
}
defer foo()
foo = func() {
fmt.Println("I am function foo2")
}

// 案例2
x := 10
defer func(a int) {
fmt.Println(a)
}(x)    
x++

// 案例3
x := 10
defer func() {
    fmt.Println(x)   // 保留x的地址
}()
x++

当执行defer语句时,函数调用不会马上发生,会先把defer注册的函数及变量拷贝到defer栈中保存,直到函数return前才执行defer中的函数调用。需要格外注意的是,这一拷贝拷贝的是那一刻函数的值和参数的值。注册之后再修改函数值或参数值时,不会生效。

defer执行时机

在Go语言的函数 return 语句不是原子操作,而是被拆成了两步

go 复制代码
rval = xxx
ret

而 defer 语句就是在这两条语句之间执行,也就是

go 复制代码
rval = xxx
defer_func
ret rval


defer x = 100
x := 10
return x  // rval=10.   x = 100, ret rval

经典面试题:

go 复制代码
package main

import "fmt"

func f1() int {
    i := 5
    defer func() {
        i++
    }()
    return i
}
func f2() *int {

    i := 5
    defer func() {
        i++
        fmt.Printf(":::%p\n", &i)
    }()
    fmt.Printf(":::%p\n", &i)
    return &i
}

func f3() (result int) {
    defer func() {
        result++
    }()
    return 5 // result = 5;ret result(result替换了rval)
}

func f4() (result int) {
    defer func() {
        result++
    }()
    return result // ret result变量的值
}

func f5() (r int) {
    t := 5
    defer func() {
        t = t + 1
    }()
    return t // ret r = 5 (拷贝t的值5赋值给r)
}

func f6() (r int) {
    fmt.Println(&r)
    defer func(r int) {
        r = r + 1
        fmt.Println(&r)
    }(r)
    return 5
}

func f7() (r int) {
    defer func(x int) {
        r = x + 1
    }(r)
    return 5
}

func main() {

    // println(f1())
    // println(*f2())
    // println(f3())
    // println(f4())
    // println(f5())
    // println(f6())
    // println(f7())

}

在命名返回方式中,最终函数返回的就是命名返回变量的值,因此,对该命名返回变量的修改会影响到最终的函数返回值!

递归函数

一种计算过程,如果其中每一步都要用到前一步或前几步的结果,称为递归的。用递归过程定义的函数,称为递归函数,例如连加、连乘及阶乘等。

go 复制代码
递归特性:

调用自身函数
必须有一个明确的结束条件
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,
每当进入一个函数调用,栈就会加一层栈帧,每当函数返 回,栈就会减一层栈帧。
由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
go 复制代码
package main

import "fmt"

func factorial(n int)int{
    if n == 0{
        return 1
    }
    return n * factorial(n-1)

}

func main() {

    // 计算n的阶乘,即 n!
    var ret = factorial(4)
    fmt.Println(ret)
}

这个数列生成规则很简单,每一项都是前两项的和,举例 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233......

go 复制代码
package main

import "fmt"

func fib(n int) int {
    if n == 2 || n == 1 {
        return 1
    }
    return fib(n-1) + fib(n-2)

}

func main() {

    // 计算n的阶乘,即 n!
    ret:=fib(6)
    fmt.Println(ret)
}

练习题



go 复制代码
package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

// 构建数据存储结构
var customers []map[string]interface{}
var customersId int

func findById(id int) int {
    index := -1
    //遍历this.customers切⽚
    for i := 0; i < len(customers); i++ {

        if customers[i]["cid"] == id {
            index = i
        }

    }
    return index
}

func isBack() bool {
    // 引导用户选择继续还是返回
    fmt.Print("请问是否返回上一层【Y/N】:")
    var backChoice string
    fmt.Scan(&backChoice)
    if strings.ToUpper(backChoice) == "Y" {
        return true
    } else {
        return false
    }
}

func inputInfo() (string, string, int8, string) {
    var name string
    fmt.Print("请输入客户姓名:")
    fmt.Scan(&name)

    var gender string
    fmt.Print("请输入客户性别:")
    fmt.Scan(&gender)

    var age int8
    fmt.Print("请输入客户年龄:")
    fmt.Scan(&age)

    var email string
    fmt.Print("请输入客户邮箱:")
    fmt.Scan(&email)

    return name, gender, age, email

}

func addCustomer() {
    for true {
        // 引导用户输入学号和姓名
        fmt.Printf("\033[1;35;40m%s\033[0m\n", "---------------------------添加客户开始-----------------------------")
        name, gender, age, email := inputInfo()
        // 创建客户的map对象
        customersId++ // 客户编号不需要输入,系统自增即可
        newCustomer := map[string]interface{}{
            "cid":    customersId,
            "name":   name,
            "gender": gender,
            "age":    age,
            "email":  email,
        }
        // 添加客户map对象添加到客户切片中
        customers = append(customers, newCustomer)
        fmt.Printf("\033[1;35;40m%s\033[0m\n", "---------------------------添加客户完成-----------------------------")
        b := isBack()
        if b {
            break
        }
    }
}

func listCustomer() {
    for true {
        fmt.Printf("\033[1;32;40m%s\033[0m\n", "----------------------------------客户列表开始-----------------------------------")
        for _, customer := range customers {
            fmt.Printf("编号:%-8d 姓名:%-8s 性别:%-8s 年龄:%-8d 邮箱:%-8s \n",
                customer["cid"], customer["name"], customer["gender"], customer["age"], customer["email"])
        }
        fmt.Printf("\033[1;32;40m%s\033[0m\n", "----------------------------------客户列表完成-----------------------------------")
        b := isBack()
        if b {
            break
        }
    }
}
func updateCustomer() {
    fmt.Printf("\033[1;36;40m%s\033[0m\n", "---------------------------客户修改开始----------------------------")
    for true {
        var updateCid int
        fmt.Print("请输入更新客户编号:")
        fmt.Scan(&updateCid)
        updateIndex := findById(updateCid)
        if updateIndex == -1 {
            fmt.Println("删除失败,输入的编号ID不存在")
            continue
        }
        fmt.Println("请输入修改客户的信息")
        name, gender, age, email := inputInfo()

        customers[updateIndex]["name"] = name
        customers[updateIndex]["gender"] = gender
        customers[updateIndex]["age"] = age
        customers[updateIndex]["email"] = email

        fmt.Printf("\033[1;36;40m%s\033[0m\n", "---------------------------客户修改完成----------------------------")
        b := isBack()
        if b {
            break
        }
    }
}

func deleteCustomer() {
    fmt.Printf("\033[1;31;40m%s\033[0m\n", "---------------------------删除客户开始----------------------------")
    var delCid int
    fmt.Print("请输入删除客户编号:")
    fmt.Scan(&delCid)

    delIndex := findById(delCid)
    if delIndex == -1 {
        fmt.Println("删除失败,输入的编号ID不存在")
        return
    }

    customers = append(customers[:delIndex], customers[delIndex+1:]...)
    fmt.Printf("\033[1;31;40m%s\033[0m\n", "---------------------------删除客户完成----------------------")

}

var data = make(map[string]map[string]string)

func main() {

    for true {
        fmt.Printf("\033[1;33;40m%s\033[0m\n", `
----------------客户信息管理系统--------------
   1、添加客户
   2、查看客户
   3、更新客户
   4、删除客户
   5、退出
-------------------------------------------
`)

        var choice int
        fmt.Printf("\033[1;38;40m%s\033[0m", "请输入选择【1-5】:")
        stdin := bufio.NewReader(os.Stdin)
        fmt.Fscan(stdin, &choice)

        switch choice {
        case 1:
            addCustomer()
        case 2:
            listCustomer()
        case 3:
            updateCustomer()
        case 4:
            deleteCustomer()
        default:
            fmt.Println("非法输入!")
            os.Exit(0)
        }
    }

}
相关推荐
ifanatic5 小时前
[面试]-golang基础面试题总结
面试·职场和发展·golang
懒是一种态度5 小时前
Golang 调用 mongodb 的函数
数据库·mongodb·golang
XINGTECODE6 小时前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang
入 梦皆星河6 小时前
在 Ubuntu/Debian 上安装 Go
ubuntu·golang·debian
凡人的AI工具箱6 小时前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
007php00720 小时前
GoZero 上传文件File到阿里云 OSS 报错及优化方案
服务器·开发语言·数据库·python·阿里云·架构·golang
高 朗21 小时前
【GO基础学习】基础语法(2)切片slice
开发语言·学习·golang·slice
IT书架21 小时前
golang面试题
开发语言·后端·golang
醒过来摸鱼1 天前
【Golang】协程
开发语言·后端·golang
灼华十一1 天前
算法编程题-排序
数据结构·算法·golang·排序算法