(上篇)
原文地址:https://www.jianshu.com/p/b23691cd8028
1. 写出下面代码输出内容
package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() {
fmt.Println("打印前")
}()
defer func() {
fmt.Println("打印中")
}()
defer func() {
fmt.Println("打印后")
}()
panic("触发异常")
}
在这个案例中,触发异常这几个字打印的顺序其实是不确定的。defer, panic, recover一般都会配套使用来捕捉异常。先看下面的案例:
- 案例一
package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() {
fmt.Println("打印前")
}()
defer func() {
fmt.Println("打印中")
}()
defer func() { // 必须要先声明defer,否则recover()不能捕获到panic异常
if err := recover();err != nil {
fmt.Println(err) //err 就是panic传入的参数
}
fmt.Println("打印后")
}()
panic("触发异常")
}
输出内容为:
触发异常
打印后
打印中
打印前
Process finished with exit code 0
- 案例二
package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() {
fmt.Println("打印前")
}()
defer func() { // 必须要先声明defer,否则recover()不能捕获到panic异常
if err := recover();err != nil {
fmt.Println(err) //err 就是panic传入的参数
}
fmt.Println("打印中")
}()
defer func() {
fmt.Println("打印后")
}()
panic("触发异常")
}
输出内容为:
打印后
触发异常
打印中
打印前
Process finished with exit code 0
- 案例三
package main
import (
"fmt"
)
func main() {
defer_call()
}
func defer_call() {
defer func() {
if err := recover();err != nil {
fmt.Println(err) //err 就是panic传入的参数
}
fmt.Println("打印前")
}()
defer func() { // 必须要先声明defer,否则recover()不能捕获到panic异常
if err := recover();err != nil {
fmt.Println(err) //err 就是panic传入的参数
}
fmt.Println("打印中")
}()
defer func() {
if err := recover();err != nil {
fmt.Println(err) //err 就是panic传入的参数
}
fmt.Println("打印后")
}()
panic("触发异常")
}
输出内容为:
触发异常
打印后
打印中
打印前
Process finished with exit code 0
总结:
defer函数属延迟执行,延迟到调用者函数执行return命令前被执行。多个defer之间按LIFO先进后出顺序执行。Go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。- 如果同时有多个
defer,那么异常会被最近的recover()捕获并正常处理。
2. 以下代码有什么问题,说明原因
package main
import (
"fmt"
)
type student struct {
Name string
Age int
}
func pase_student() map[string]*student {
m := make(map[string]*student)
stus := []student{
{Name: "zhou", Age: 24},
{Name: "li", Age: 23},
{Name: "wang", Age: 22},
}
for _, stu := range stus {
m[stu.Name] = &stu
}
return m
}
func main() {
students := pase_student()
for k, v := range students {
fmt.Printf("key=%s,value=%v \n", k, v)
}
}
运行结果:
key=zhou,value=&{wang 22}
key=li,value=&{wang 22}
key=wang,value=&{wang 22}
Process finished with exit code 0
修改一下代码:
将下面的代码:
for _, stu := range stus {
m[stu.Name] = &stu
}
修改为:
for _, stu := range stus {
fmt.Printf("%v\t%p\n",stu,&stu)
m[stu.Name] = &stu
}
运行结果为:
{shen 24} 0xc4200a4020
{li 23} 0xc4200a4020
{wang 22} 0xc4200a4020
key=shen,value=&{wang 22}
key=li,value=&{wang 22}
key=wang,value=&{wang 22}
Process finished with exit code 0
通过上面的案例,我们不难发现stu变量的地址始终保持不变,每次遍历仅进行struct值拷贝,故m[stu.Name]=&stu实际上一直指向同一个地址,最终该地址的值为遍历的最后一个struct的值拷贝。
形同如下代码:
var stu student
for _, stu = range stus {
m[stu.Name] = &stu
}
修正方案,取数组中原始值的地址:
for i, _ := range stus {
stu:=stus[i]
m[stu.Name] = &stu
}
重新运行,效果如下:
{shen 24} 0xc42000a060
{li 23} 0xc42000a0a0
{wang 22} 0xc42000a0e0
key=shen,value=&{shen 24}
key=li,value=&{li 23}
key=wang,value=&{wang 22}
Process finished with exit code 0
3. 下面的代码会输出什么,并说明原因
package main
import (
"fmt"
"runtime"
"sync"
)
func ini

本文是一份关于Golang的笔试题集合,涵盖了代码输出、异常处理、并发安全、循环陷阱等多个方面。通过分析代码示例,讲解了延迟执行、变量作用域、接口实现、并发控制等核心概念,帮助读者理解和避免编程中常见的问题。
最低0.47元/天 解锁文章
1066

被折叠的 条评论
为什么被折叠?



