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

news2024/11/29 3:53:11

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 rval


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

经典面试题:

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())

}

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

递归函数

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

递归特性:

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

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)
}

在这里插入图片描述

练习题

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

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)
        }
    }

}

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

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

相关文章

密码学学习笔记(十四):SHA-3 Sponge Construction - 海绵结构

SHA-3算法满足了哈希函数的三个安全属性&#xff0c;并且和SHA-2的变体达到同样级别的安全性。此外&#xff0c;SHA-3算法不容易受到长度扩展攻击&#xff0c;并可用于计算秘密消息的哈希值。 SHA-3是一种建立在Permutation(置换)之上的密码算法。 置换就是假设有两个数组a和…

Hadoop第一课之环境配置

1.配置一个模板机 要求&#xff1a;IP DNS地址页 网址 防火墙 安装包 1.ip ifconfig 查询 先用虚拟机看一下自己的网关 vim search/provides 命令 查找 # 修改网络配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33 如果提示找不到vim命令&#xff0c;使用yum下载v…

Springboot Excel 最简单的 多sheet表 导入导出

前言 上周学习群里有人问到&#xff0c;多个sheet的导出导入&#xff0c;我第一反应就是easypoi不是自己就有方法了么&#xff1f; 后面一想&#xff0c;可能有些看客还处于是 找工具类&#xff0c;然后调试 的写代码 的 阶段&#xff0c;可能还不会去看jar包的一些函数。 既然…

SpringMVC入门篇5 --- 拦截器

目录 1. 简介 拦截器&#xff08;Interceptor&#xff09;是一种动态拦截方法调用的机制。 作用&#xff1a; 在指定的方法调用前后执行预先设定后的代码。阻止原始方法的执行。 拦截器与过滤器的区别 归属不同&#xff1a;Filter属于Servlet技术&#xff0c;Interceptor…

使用vue3 + Ts + Vite + ElementPlus实现一个抽奖程序

一. 说明 这是一个通过vue3 Ts Vite ElementPlus实现的一个抽奖程序。项目链接 二. 整体架构与功能描述 左侧设置了奖品说明&#xff0c;每个奖项配有文字和图片简介。总共设置了四个奖项&#xff0c;分别是特等奖1名&#xff0c;一等奖2名&#xff0c;二等奖5名&#xf…

平安养老险党委书记、董事长甘为民:聚焦养老主业 助推养老保障事业高质量发展

每经记者 涂颖浩 每经编辑 马子卿 随着人口老龄化趋势加剧&#xff0c;中国养老金融市场呈现出巨大的潜力&#xff0c;逐步迈入养老新时代。近日&#xff0c;平安养老险党委书记、董事长甘为民在接受《每日经济新闻》记者专访时表示&#xff0c;过往单纯的养老发展模式难以满足…

Jmeter性能测试 —— 性能测试的概念

性能测试的概念 性能测试是指通过特定方式&#xff0c;对被测系统按照一定策略施加压力&#xff0c;获取系统 响应时间、TPS&#xff08;Transaction Per Second&#xff09;、吞吐量、资源利用率等性能指标&#xff0c;以期保证生产系统的性能能够满足用户需求的过程。 性能…

浅析编译与链接

生成可执行文件的四个过程 当编写和构建计算机程序时&#xff0c;预处理、编译、汇编和链接是将源代码转化为可执行程序的关键过程。以下是对每个阶段的详细解释&#xff1a; 1. 预处理&#xff08;Preprocessing&#xff09;&#xff1a;将.c/.cpp文件中的头文件展开、宏展开…

【PostgreSQL内核学习(一)—— Ubuntu源码安装PostgreSQL】

Ubuntu源码安装PostgreSQL 1. PostgreSQL官网下载压缩包2. 解压&安装2.1 解压文件2.2 安装依赖2.3 执行安装2.4 执行安装2.5 添加路径到文件 3. 初始化数据库与使用3.1 初始化数据库3.2 启动数据库服务3.3 启动数据库 1. PostgreSQL官网下载压缩包 下载地址&#xff1a;ht…

【黑马头条之freemarker入门】

本笔记内容为黑马头条项目的freemarker部分 目录 一、freemarker 介绍 二、环境搭建&&快速入门 1、创建测试工程 2、配置文件 3、创建模型类 4、创建模板 5、创建controller 6、创建启动类 7、测试 三、freemarker基础 1、基础语法种类 2、集合指令&#…

【iOS】—— 面向对象,Runtime,ARC等问题总结

对于暑假学习大多数是对之前学习的一个复习&#xff0c;在这里只做对之前学习欠缺知识的补充以及这些知识点涉及的一些问题&#xff0c;从问题入手学习。 文章目录 面向对象1.一个NSObject对象占多少内存&#xff1f;2.对象的isa指针指向哪里&#xff1f;3.OC的类信息存放在哪…

PLSQL编程

1.概念和目的 1.1. 什么是PL/SQL? PL/SQL&#xff08;Procedure Language/SQL&#xff09; 是Oracle对sql语言的过程化扩展 (类似于Basic)&#xff1b; 指在SQL命令语言中增加了过程处理语句&#xff08;如分支、循环等&#xff09;&#xff0c;使SQL语言具有过程处理能力。…

Spring @Autowired 注解原理

Spring Autowired 注解原理 1.Autowired 使用 ComponentScan("org.example.bean") public class AnnoContextDemo {Autowiredprivate User user;public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplic…

Ultipa嬴图数据库 | 深圳国际金融科技大赛圆满落幕

2023年7月13日&#xff0c;由深圳市地方金融监督管理局、深圳市福田区人民政府、深圳市南山区人民政府指导&#xff0c;招商局金融科技有限公司主办的2022深圳国际金融科技大赛总决赛在福田区圆满落幕。经过从初赛到决赛&#xff0c;共计103个项目的激烈角逐&#xff0c;Ultipa…

Unity视角拉近时物体缺失的问题处理

在Unity的开发过程中&#xff0c;我们可能会遇到以下情况&#xff1a; 就是在场景的不断编辑中&#xff0c;突然又一次打开场景&#xff0c;再拉近或拉远场景视角时&#xff0c;会出现场景中的对象会显示不全的问题。 出现了这样的情况会让场景的预览很不友好。 出现这个问题的…

【006】面向 6G 的深度图像语义通信模型

摘要 目前的语义通信模型在处理图像数据方面仍有可改善的部分&#xff0c;包括有效的图像语义编解码、高效的语义模型训练和精准的图像语义评估。为此&#xff0c;提出了一种深度图像语义通信&#xff08;DeepISC&#xff09;模型。首先采用基于 vision transformer 的自编码器…

数字IC后端设计实现中的Post-mask ECO应该怎么做?

在数字IC后端设计实现中&#xff0c;我们经常会涉及到芯片需要做Function ECO。常见的Function ECO可以分为pre mask ECO和post mask ECO两种。因此&#xff0c;作为一个数字IC后端工程师&#xff0c;必须熟练掌握这两种Function ECO的实现流程及其实现技巧。 两者的区别在于&…

栈和队列【数据结构】

1、栈 &#xff08;1&#xff09;Stack.h #pragma once #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <stdbool.h>typedef int STDataType;typedef struct Stack {STDataType* a;int top;int capacity; }ST;void STInit(ST*…

[JVM] 5. 运行时数据区(2)-- 程序计数器(Program Counter Register)

一、概述 JVM中的程序计数器&#xff08;Program Counter Register&#xff09;是对物理PC寄存器的一种抽象模拟。它是一块很小的内存空间&#xff0c;几乎可以忽略不记。也是运行速度最快的存储区域。在 JVM 规范中&#xff0c;每个线程都有它自己的程序计数器&#xff0c;是…

.nvmrc无效

背景 既然你已经使用了nvm那么他的功能我就不介绍了。但是使用场景我与开发小伙伴探讨了一下发现很多问题。你的nvm使用方法真的正确吗&#xff1f; 问题&#xff1a;假设现在有10个项目对应10个不同的node版本&#xff0c;你应该怎么来管理呢&#xff1f; 同学1&#xff1a; …