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

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

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

fmt.Println("test01")
defer fmt.Println("test02")
fmt.Println("test03")

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

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

多个defer执行顺序

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

fmt.Println("test01")
defer fmt.Println("test02")
fmt.Println("test03")
defer fmt.Println("test04")
fmt.Println("test05")

defer的拷贝机制

// 案例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 语句不是原子操作,而是被拆成了两步

rval = xxx
ret

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

rval = xxx
defer_func
ret rvaldefer x = 100
x := 10
return x  // rval=10.   x = 100, ret rval

经典面试题:

package mainimport "fmt"func f1() int {i := 5defer func() {i++}()return i
}
func f2() *int {i := 5defer 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 := 5defer 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 + 1fmt.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())}

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

递归函数

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

递归特性:调用自身函数
必须有一个明确的结束条件
在计算机中,函数调用是通过栈(stack)这种数据结构实现的,
每当进入一个函数调用,栈就会加一层栈帧,每当函数返 回,栈就会减一层栈帧。
由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
package mainimport "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……

package mainimport "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)
}

在这里插入图片描述

练习题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

package mainimport ("bufio""fmt""os""strings"
)// 构建数据存储结构
var customers []map[string]interface{}
var customersId intfunc 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 stringfmt.Scan(&backChoice)if strings.ToUpper(backChoice) == "Y" {return true} else {return false}
}func inputInfo() (string, string, int8, string) {var name stringfmt.Print("请输入客户姓名:")fmt.Scan(&name)var gender stringfmt.Print("请输入客户性别:")fmt.Scan(&gender)var age int8fmt.Print("请输入客户年龄:")fmt.Scan(&age)var email stringfmt.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 intfmt.Print("请输入更新客户编号:")fmt.Scan(&updateCid)updateIndex := findById(updateCid)if updateIndex == -1 {fmt.Println("删除失败,输入的编号ID不存在")continue}fmt.Println("请输入修改客户的信息")name, gender, age, email := inputInfo()customers[updateIndex]["name"] = namecustomers[updateIndex]["gender"] = gendercustomers[updateIndex]["age"] = agecustomers[updateIndex]["email"] = emailfmt.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 intfmt.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 intfmt.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)}}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/27009.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SpringBoot+vue的校园闲置物品租售系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

h5live 2.0.1 合入测试

直接超过1个G 有消息进来&#xff0c;就是不显示

2023年知识库软件爆火的有哪些?

2023年知识库软件爆火的可能性有很多&#xff0c;以下是一些可能的候选者&#xff1a; 一、Baklib&#xff1a;Baklib是一款新兴的知识库软件&#xff0c;它提供了强大的知识管理和组织功能。它能够帮助用户收集、整理和共享知识&#xff0c;并提供智能搜索和推荐引擎&#xf…

Docker容器常用命令大全:熟练掌握使容器优化更加高效

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

微服务-Nacos环境安装

文章目录 1. 微服务1.1 微服务概括 2. 微服务框架2.1 Spring Cloud2.2 Spring Cloud alibaba/Spring Cloud Netflix2.3微服务框架组件(alibaba) 3 Nacos3.1 Nacos介绍3.3 Naocs工作结构3.3 Nacos功能3.4 环境准备下载安装 1. 微服务 1.1 微服务概括 单体架构有问题,所以做项目…

设计模式之桥接模式

写在前面 本文看下桥接设计模式。 1&#xff1a;介绍 1.1&#xff1a;什么时候桥接设计模式 当一个业务场景由多个变化维度组成&#xff0c;并且这多个变化的维度到底有多少种情况是不确定&#xff0c;比如现在我们要为瑞幸咖啡写一个系统&#xff0c;很自然的&#xff0c;…

云计算的学习(四)

四、云计算中的存储基础知识 1.云计算虚拟化中的存储架构 ①虚拟化存储 在虚拟化存储架构中&#xff0c;最底层为物理磁盘。 底层的硬件组成存储池&#xff0c;存储池分为NAS存储和SAN存储&#xff1b;NAS存储需要文件系统&#xff1b;SAN存储需要对存储池进行逻辑划分产生逻…

Stability AI 把绘画门槛打为 0!

本文由 GPT- 4 所创作&#xff0c;配图由 Stable Doodle 生成。 编者按 Stability AI 上新了&#xff01; 其收购的 Clipdrop 发布了全新的 Stable Doodle 工具&#xff0c;我在使用后最为直观的感受就是 —— 把绘画门槛打下来了。 在 Stable Doodle 之前&#xff0c;使用各…

动手学深度学习v2 p2 线性神经网络 线性回归

3. 线性神经网络 回归&#xff08;regression&#xff09;是能为一个或多个自变量与因变量之间关系建模的一类方法。 在自然科学和社会科学领域&#xff0c;回归经常用来表示输入和输出之间的关系。 在机器学习领域中的大多数任务通常都与预测&#xff08;prediction&#xf…

node.js 第一天

目录 使用readFile()方法读取文件内容 判断文件是否读取成功 判断文件是否写入成功 案例 考试成绩整理 path path.join&#xff08;&#xff09; path.basename() 使用readFile()方法读取文件内容 // 1. 导入 fs 模块&#xff0c;来操作文件 const fs require(fs)// 2.…

(ceph)资源池poll管理

资源池 Pool 管理 前面的文章中我们已经完成了 Ceph 集群的部署&#xff08;ceph部署: 传送门&#xff09;&#xff0c;但是我们如何向 Ceph 中存储数据呢&#xff1f;首先我们需要在 Ceph 中定义一个 Pool 资源池。Pool 是 Ceph 中存储 Object 对象抽象概念。我们可以将其理解…

Jmeter(119)-函数threadNum妙用

今天的接口场景是&#xff1a;有N个用户需要每隔5秒去查询一次数据&#xff0c;也就是说N个用户会去循环执行同一个接口。一开始的时候将用户参数化时使用了counter&#xff0c; 要执行2个线程3次循环&#xff0c;发现每次循环时&#xff0c;接口中用户参数的数据就会不一样&am…