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.mzph.cn/news/4397.shtml

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

相关文章

netty组件详解-上

netty服务端示例: private void doStart() throws InterruptedException {System.out.println("netty服务已启动");// 线程组EventLoopGroup group new NioEventLoopGroup();try {// 创建服务器端引导类ServerBootstrap server new ServerBootstrap();// 初始化服…

苹果APP安装包ipa如何安装在手机上

苹果APP安装包ipa如何安装在手机上 苹果APP的安装比安卓复杂且困难&#xff0c;很多人不知道如何将ipa文件安装到手机上。以下是几种苹果APP安装在iOS设备的方式&#xff0c;供大家参考。 一、上架App Store 这是最正规的方式。虽然审核过程复杂、时间较长&#xff0c;且审核…

数据可视化组件有什么用?

数据可视化组件在数据分析中扮演着至关重要&角色。 通过图表、图形和交互式界面&#xff0c;数据可视化组件帮助将复杂的数据转化为易于理解的视觉展示。这种形式的数据呈现有助于发现模式、趋势和异常&#xff0c;并能够快速有效地传达数据的含义和洞察。 下面简单举两个…

使用Visual Studio打造强大的程序,从添加第三方库开始

使用Visual Studio打造强大的程序&#xff0c;从添加第三方库开始 博主简介一、引言二、理解第三方库三、下载和安装第三方库四、示例代码和演示五、总结 博主简介 &#x1f4a1;一个热爱分享高性能服务器后台开发知识的博主&#xff0c;目标是通过理论与代码实践的结合&#x…

【状态估计】基于FOMIAUKF、分数阶模块、模型估计、多新息系数的电池SOC估计研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringBoot项目中MVC使用--【JSB系列之010】

SpringBoot系列文章目录 SpringBoot知识范围-学习步骤【JSB系列之000】 文章目录 SpringBoot系列文章目录Http协议是马冬梅Cookie机制Session机制Token MVC模型本章的专注内容UserController代码 ThymeleafLets GO!总结作业配套资源题外话 Http协议是马冬梅 HTTP简介 1. HTTP…

润和软件与华秋达成生态共创合作,共同推动物联网硬件创新

7月11日&#xff0c;在2023慕尼黑上海电子展现场&#xff0c;江苏润开鸿数字科技有限公司(以下简称“润开鸿”)与深圳华秋电子有限公司(以下简称“华秋”)签署了生态共创战略合作协议&#xff0c;共同推动物联网硬件生态繁荣发展。当前双方主要基于润开鸿的硬件产品及解决方案开…

完整的电商平台后端API开发总结

对于开发一个Web项目来说&#xff0c;无论是电商还是其他品类的项目&#xff0c;注册与登录模块都是必不可少的&#xff1b;注册登录功能也是我们在日常生活中最长接触的&#xff0c;对于这个业务场景的需求与逻辑大概是没有什么需要详细介绍的&#xff0c;市面上常见的邮箱注册…

混合背包(01+完全+多重背包大杂烩)

因为我们知道求解多重背包时&#xff0c;是将其进行二进制优化为01背包问题&#xff0c;那么我们就将01背包和多重背包看成一种情况&#xff0c;然后只要处理&#xff0c;完全背包和01背包问题即可&#xff08;详细看下方代码&#xff09; #include<bits/stdc.h> using n…

【ArcGIS Pro二次开发】(47):要素类追加至空库(批量)

本工具主要是针对国空数据入库而做的。 如果你手头已经整理了一部分要素类数据&#xff0c;但是数据格式&#xff0c;字段值可能并没有完全按照规范设置好&#xff0c;需要将这些数据按规范批量和库&#xff0c;就可以尝试用这个工具。 准备数据&#xff1a;标准空库、你已做…

Python、Selenium实现问卷星自动填写(内含适配个人问卷的方法)

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Py…

SpringMVC的数据响应-直接回写json字符串

一般我们操作对象&#xff0c;将对象转变为json 这时导入json 转换工具的包 包1 包2-json数据绑定 包3 返回的就是json字符串你直接返回就行了 返回一个json格式的字符串 直接回写就加这个res.... 内部字符串要进行相应的转意 能够看到json字符串 能不能你封装对象&#xff0c…

【蓝图】p28-p29按键+鼠标点击实现开关门

p28-p29按键鼠标点击实现开关门 p28&#xff0c;创建门的蓝图类创建一个Actor注意&#xff08;当门的中心点不在边角上时&#xff09; 蓝图三个旋转区别按E键开关门使鼠标点击也可以开门可能遇到的bug问题 p28&#xff0c;创建门的蓝图类 actor和组件的区别、门的轴心点修改 …

【Ajax】笔记-取消请求

在进行AJAX(Asynchronous JavaScript and XML) 请求时&#xff0c;有时候我们需要取消正在进行的请求。取消请求可以帮助我们提高用户体验&#xff0c;病减少不必要的网络流量和服务器负载。 取消请求的方法 在AJAX请求中&#xff0c;我们可以使用以下方法来取消正在进行的请求…

golang 日志库logrus和lumberjack 日志切割库实践

package mainimport (log "github.com/Sirupsen/logrus""gopkg.in/natefinch/lumberjack.v2" )func main() {logger : &lumberjack.Logger{// 日志输出文件路径Filename: "/var/log/myapp/foo.log",// 日志文件最大 size, 单位是 MBMaxSiz…

数字 IC 设计职位经典笔/面试题(二)

共100道经典笔试、面试题目&#xff08;文末可全领&#xff09; FPGA 中可以综合实现为 RAM/ROM/CAM 的三种资源及其注意事项&#xff1f; 三种资源&#xff1a;BLOCK RAM&#xff0c;触发器&#xff08;FF&#xff09;&#xff0c;查找表&#xff08;LUT&#xff09;&#xf…

ROS:pluginlib

目录 一、前言二、概念三、作用四实际用例4.1需求4.2流程4.3准备4.4创建基类4.5创建插件4.6注册插件4.7构建插件库4.8使插件可用于ROS工具链4.8.1配置xml4.8.2导出插件 4.9使用插件4.10执行 一、前言 pluginlib直译是插件库&#xff0c;所谓插件字面意思就是可插拔的组件&…

河北幸福消费金融基于 Apache Doris 构建实时数仓,查询提速 400 倍!

本文导读&#xff1a; 随着河北幸福消费金融的客户数量和放贷金额持续上升&#xff0c;如何依托大数据、数据分析等技术来提供更好决策支持、提高工作效率和用户体验&#xff0c;成为了当前亟需解决的问题。基于此&#xff0c;公司决定搭建数据中台&#xff0c;从基于 TDH 的离…

Windows 如何锁定文件

一、背景 如果应用程序有操作本地文件的功能&#xff08;如&#xff1a;读、写、复制、移动、删除等等&#xff09;&#xff0c;那么在测试或调试该应用程序时&#xff0c;肯定需要测试文件被其他应用程序锁定时&#xff0c;你的应用程序是如何处理的。 那么如何在本地模拟文件…

Ceph(分布式文件系统)

Ceph(分布式文件系统) 1、存储基础 单机存储设备 ●DAS&#xff08;直接附加存储&#xff0c;是直接接到计算机的主板总线上去的存储&#xff09; IDE、SATA、SCSI、SAS、USB 接口的磁盘 所谓接口就是一种存储设备驱动下的磁盘设备&#xff0c;提供块级别的存储 ●NAS&#xf…