当前位置: 首页 > news >正文

【Golang 面试基础题】每日 5 题(十)

  1.  ✍个人博客:Pandaconda-CSDN博客

📣专栏地址:http://t.csdnimg.cn/UWz06

📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

46. Go  方法值接收者和指针接收者的区别?

在 Go 中,方法可以定义在结构体类型上。接收者是指在方法定义中声明的函数参数。接收者可以是值接收者,也可以是指针接收者。值接收者在方法调用时会对接收者进行复制,而指针接收者则会使用指针来引用原始接收者。

使用值接收者时,方法中对接收者所做的任何修改都不会影响原始接收者。而使用指针接收者时,方法中对接收者所做的任何修改都将影响原始接收者。

另外,指针接收者的优势在于它可以避免在每次调用方法时复制接收者,从而提高程序的性能。此外,在某些情况下,只有使用指针接收者才能修改接收者的状态,因为值接收者只能修改接收者的副本。

例如,以下代码演示了一个使用值接收者和指针接收者的方法:

type Counter struct {count int
}// 值接收者方法
func (c Counter) increment() {c.count++
}// 指针接收者方法
func (c *Counter) decrement() {c.count--
}func main() {// 值接收者方法不会改变原始接收者的值c1 := Counter{count: 0}c1.increment()fmt.Println(c1.count) // 输出 0// 指针接收者方法会改变原始接收者的值c2 := Counter{count: 0}c2.decrement()fmt.Println(c2.count) // 输出 -1
}

在上面的示例中,increment() 方法使用值接收者,而 decrement() 方法使用指针接收者。在调用 increment() 方法后,原始 Counter 结构体实例的 count 属性保持为零,因为该方法对接收者的修改只影响了接收者的副本。而在调用 decrement() 方法后,原始 Counter 结构体实例的 count 属性减少了一,因为该方法直接修改了原始接收者。

 47. Go 函数返回局部变量的指针是否安全?

一般来说,局部变量在函数返回后被销毁,因此被返回的引用就成为了 "无所指" 的引用,程序会进入未知状态。

但这在 Go 中是安全的,Go 编译器将会对每个局部变量进行逃逸分析。如果发现局部变量的作用域超出该函数,则不会将内存分配在栈上,而是分配在堆上,因为他们不在栈区,即使释放函数,其内容也不会受影响。

package mainimport "fmt"func add(x, y int) *int {res := 0res = x + yreturn &res
}func main() {fmt.Println(add(1, 2))
}

这个例子中,函数 add 局部变量 res 发生了逃逸。res 作为返回值,在 main 函数中继续使用,因此 res 指向的内存不能够分配在栈上,随着函数结束而回收,只能分配在堆上。

编译时可以借助选项 -gcflags=-m,查看变量逃逸的情况。

./main.go:6:2: res escapes to heap:
./main.go:6:2:   flow: ~r2 = &res:
./main.go:6:2:     from &res (address-of) at ./main.go:8:9
./main.go:6:2:     from return &res (return) at ./main.go:8:2
./main.go:6:2: moved to heap: res
./main.go:12:13: ... argument does not escape
0xc0000ae008

res escapes to heap 即表示 res 逃逸到堆上了。

48. def er 的执行顺序是什么? defer的作用和特点是什么?

在 Go 语言中,defer 是一种延迟执行机制,用于在函数退出前执行一些特定的代码,无论是函数正常返回还是发生异常。defer 语句是在函数调用结束后执行的,即使出现错误或 panic 也会执行。defer 可以用于清理资源、处理错误等场景。

defer 语句的执行顺序是 “后进先出” 的,也就是说最后一个被 defer 的语句会最先执行,直到第一个被 defer 的语句执行完毕为止。

例如,下面的代码中,defer 语句的执行顺序是 3、2、1。

func example() {defer fmt.Println("1")defer fmt.Println("2")defer fmt.Println("3")fmt.Println("done")
}

需要注意的是,defer 延迟执行的代码并不是在函数退出前立即执行,而是在函数执行结束后,当函数返回时才会执行。因此,如果在 defer 语句中使用的变量在函数返回前发生了改变,那么最终执行的代码将使用最终值。

49. Go  defer 关键字的实现原理?

定义

defer 能够让我们推迟执行某些函数调用,推迟到当前函数返回前才实际执行。defer 与 panic 和 recover 结合,形成了 Go 语言风格的异常与捕获机制。

使用场景

defer 语句经常被用于处理成对的操作,如文件句柄关闭、连接关闭、释放锁。

优点:

方便开发者使用。

缺点:

有性能损耗。

实现原理

Go1.14 中编译器会将 defer 函数直接插入到函数的尾部,无需链表和栈上参数拷贝,性能大幅提升。把 defer 函数在当前函数内展开并直接调用,这种方式被称为 open coded defer。

源代码:

func A(i int) {defer A1(i, 2*i)if(i > 1) {defer A2("Hello", "eggo")}// code to do somethingreturn
}
func A1(a,b int) {//......
}
func A2(m,n string) {//......
}

编译后(伪代码):

func A(i int) {// code to do somethingif(i > 1){A2("Hello", "eggo")}A1(i, 2*i)return
}

代码示例

  1. 函数退出前,按照先进后出的顺序,执行 defer 函数

    package mainimport "fmt"// defer:延迟函数执行,先进后出
    func main() {defer fmt.Println("defer1")defer fmt.Println("defer2")defer fmt.Println("defer3")defer fmt.Println("defer4")fmt.Println("11111")
    }// 11111
    // defer4
    // defer3
    // defer2
    // defer1
    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==
  2. panic 后的 defer 函数不会被执行(遇到 panic,如果没有捕获错误,函数会立刻终止)

    package mainimport "fmt"// panic后的defer函数不会被执行
    func main() {defer fmt.Println("panic before")panic("发生panic")defer func() {fmt.Println("panic after")}()
    }// panic before
    // panic: 发生panic
    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==
  3. panic 没有被 recover 时,抛出的 panic 到当前 goroutine 最上层函数时,最上层程序直接异常终止。

    package mainimport "fmt"func F() {defer func() {fmt.Println("b")}()panic("a")
    }// 子函数抛出的panic没有recover时,上层函数时,程序直接异常终止
    func main() {defer func() {fmt.Println("c")}()F()fmt.Println("继续执行")
    }// b
    // c
    // panic: a
    wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

  4. panic 有被 recover 时,当前 goroutine 最上层函数正常执行。 

 

package mainimport "fmt"func F() {defer func() {if err := recover(); err != nil {fmt.Println("捕获异常:", err)}fmt.Println("b")}()panic("a")
}func main() {defer func() {fmt.Println("c")}()F()fmt.Println("继续执行")
}// 捕获异常: a
// b
// 继续执行
// c

相关文章:

  • 基于上云api前端开发经验教训(loading...)
  • 基于python的BP神经网络回归模型
  • RT-Thread Studio搭建 Renesa Version Board开发环境
  • Python 中数据科学和机器学习的作用
  • 如何在 SpringBoot 中优雅的做参数校验?
  • Pytorch使用教学8-张量的科学运算
  • ubuntu 22.04 安装部署gitlab详细过程
  • ubuntu新机依赖
  • [Mysql-视图和存储过程]
  • 【React】条件渲染:深入探讨高效开发技巧与最佳实践
  • Python | Leetcode Python题解之第292题Nim游戏
  • 【前端手写代码】手写Object.create
  • 在CentOS 7上安装Apache Tomcat 10.0.27
  • Logback原理及应用详解(三)
  • C++ | Leetcode C++题解之第292题Nim游戏
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • 2019年如何成为全栈工程师?
  • CSS 三角实现
  • es6要点
  • express + mock 让前后台并行开发
  • Java新版本的开发已正式进入轨道,版本号18.3
  • Mybatis初体验
  • MySQL的数据类型
  • Selenium实战教程系列(二)---元素定位
  • Spring Cloud(3) - 服务治理: Spring Cloud Eureka
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 关于List、List?、ListObject的区别
  • 记录一下第一次使用npm
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 微信小程序上拉加载:onReachBottom详解+设置触发距离
  • 吴恩达Deep Learning课程练习题参考答案——R语言版
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 因为阿里,他们成了“杭漂”
  • 用 Swift 编写面向协议的视图
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 你对linux中grep命令知道多少?
  • 翻译 | The Principles of OOD 面向对象设计原则
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • #、%和$符号在OGNL表达式中经常出现
  • #如何使用 Qt 5.6 在 Android 上启用 NFC
  • #知识分享#笔记#学习方法
  • $.each()与$(selector).each()
  • (13)Latex:基于ΤΕΧ的自动排版系统——写论文必备
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (poj1.2.1)1970(筛选法模拟)
  • (STM32笔记)九、RCC时钟树与时钟 第二部分
  • (带教程)商业版SEO关键词按天计费系统:关键词排名优化、代理服务、手机自适应及搭建教程
  • (二)构建dubbo分布式平台-平台功能导图
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (二)原生js案例之数码时钟计时
  • (二刷)代码随想录第16天|104.二叉树的最大深度 559.n叉树的最大深度● 111.二叉树的最小深度● 222.完全二叉树的节点个数
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (六)vue-router+UI组件库