切换导航
{{systemName}}
{{ info.Title }}
{{info.Title}}
{{ menu.Title }}
{{menu.Title}}
登录
|
退出
搜索
常见语法题目2
作者:ych
### 1、写出下面代码输出内容。 ```go 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` 关键字的实现跟go关键字很类似,不同的是它调用的是`runtime.deferproc`而不是`runtime.newproc`。 在`defer`出现的地方,插入了指令`call runtime.deferproc`,然后在函数返回之前的地方,插入指令`call runtime.deferreturn`。 goroutine的控制结构中,有一张表记录`defer`,调用`runtime.deferproc`时会将需要defer的表达式记录在表中,而在调用`runtime.deferreturn`的时候,则会依次从defer表中出栈并执行。 因此,题目最后输出顺序应该是`defer` 定义顺序的倒序。`panic` 错误并不能终止 `defer` 的执行。 ### 2、 以下代码有什么问题,说明原因 ```go type student struct { Name string Age int } func pase_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 } } ``` **解析:** golang 的 `for ... range` 语法中,`stu` 变量会被复用,每次循环会将集合中的值复制给这个变量,因此,会导致最后`m`中的`map`中储存的都是`stus`最后一个`student`的值。 ### 3、下面的代码会输出什么,并说明原因 ```go func main() { runtime.GOMAXPROCS(1) wg := sync.WaitGroup{} wg.Add(20) for i := 0; i < 10; i++ { go func() { fmt.Println("i: ", i) wg.Done() }() } for i := 0; i < 10; i++ { go func(i int) { fmt.Println("i: ", i) wg.Done() }(i) } wg.Wait() } ``` **解析:** 这个输出结果决定来自于调度器优先调度哪个G。从runtime的源码可以看到,当创建一个G时,会优先放入到下一个调度的`runnext`字段上作为下一次优先调度的G。因此,最先输出的是最后创建的G,也就是9. ```go func newproc(siz int32, fn *funcval) { argp := add(unsafe.Pointer(&fn), sys.PtrSize) gp := getg() pc := getcallerpc() systemstack(func() { newg := newproc1(fn, argp, siz, gp, pc) _p_ := getg().m.p.ptr() //新创建的G会调用这个方法来决定如何调度 runqput(_p_, newg, true) if mainStarted { wakep() } }) } ... if next { retryNext: oldnext := _p_.runnext //当next是true时总会将新进来的G放入下一次调度字段中 if !_p_.runnext.cas(oldnext, guintptr(unsafe.Pointer(gp))) { goto retryNext } if oldnext == 0 { return } // Kick the old runnext out to the regular run queue. gp = oldnext.ptr() } ``` ### 4、下面代码会输出什么? ```go type People struct{} func (p *People) ShowA() { fmt.Println("showA") p.ShowB() } func (p *People) ShowB() { fmt.Println("showB") } type Teacher struct { People } func (t *Teacher) ShowB() { fmt.Println("teacher showB") } func main() { t := Teacher{} t.ShowA() } ``` **解析:** 输出结果为`showA`、`showB`。golang 语言中没有继承概念,只有组合,也没有虚方法,更没有重载。因此,`*Teacher` 的 `ShowB` 不会覆写被组合的 `People` 的方法。 ### 5、下面代码会触发异常吗?请详细说明 ```go func main() { runtime.GOMAXPROCS(1) int_chan := make(chan int, 1) string_chan := make(chan string, 1) int_chan <- 1 string_chan <- "hello" select { case value := <-int_chan: fmt.Println(value) case value := <-string_chan: panic(value) } } ``` **解析:** 结果是随机执行。golang 在多个`case` 可读的时候会公平的选中一个执行。 ### 6、下面代码输出什么? ```go func calc(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } func main() { a := 1 b := 2 defer calc("1", a, calc("10", a, b)) a = 0 defer calc("2", a, calc("20", a, b)) b = 1 } ``` **解析:** 输出结果为: ``` 10 1 2 3 20 0 2 2 2 0 2 2 1 1 3 4 ``` `defer` 在定义的时候会计算好调用函数的参数,所以会优先输出`10`、`20` 两个参数。然后根据定义的顺序倒序执行。 ### 7、请写出以下输入内容 ```go func main() { s := make([]int, 5) s = append(s, 1, 2, 3) fmt.Println(s) } ``` **解析:** 输出为 `0 0 0 0 0 1 2 3`。 `make` 在初始化切片时指定了长度,所以追加数据时会从`len(s)` 位置开始填充数据。 ### 8、下面的代码有什么问题? ```go type UserAges struct { ages map[string]int sync.Mutex } func (ua *UserAges) Add(name string, age int) { ua.Lock() defer ua.Unlock() ua.ages[name] = age } func (ua *UserAges) Get(name string) int { if age, ok := ua.ages[name]; ok { return age } return -1 } ``` **解析:** 在执行 Get方法时可能被thorw。 虽然有使用sync.Mutex做写锁,但是map是并发读写不安全的。map属于引用类型,并发读写时多个协程见是通过指针访问同一个地址,即访问共享变量,此时同时读写资源存在竞争关系。会报错误信息:“fatal error: concurrent map read and map write”。 因此,在 `Get` 中也需要加锁,因为这里只是读,建议使用读写锁 `sync.RWMutex`。 ### 9、下面的迭代会有什么问题? ```go func (set *threadSafeSet) Iter() <-chan interface{} { ch := make(chan interface{}) go func() { set.RLock() for elem := range set.s { ch <- elem } close(ch) set.RUnlock() }() return ch } ``` **解析:** 默认情况下 `make` 初始化的 `channel` 是无缓冲的,也就是在迭代写时会阻塞。 ### 10、以下代码能编译过去吗?为什么? ```go package main import ( "fmt" ) type People interface { Speak(string) string } type Student struct{} func (stu *Student) Speak(think string) (talk string) { if think == "bitch" { talk = "You are a good boy" } else { talk = "hi" } return } func main() { var peo People = Student{} think := "bitch" fmt.Println(peo.Speak(think)) } ``` **解析:** 编译失败,值类型 `Student{}` 未实现接口`People`的方法,不能定义为 `People`类型。 在 golang 语言中,`Student` 和 `*Student` 是两种类型,第一个是表示 `Student` 本身,第二个是指向 `Student` 的指针。 ### 11、以下代码打印出来什么内容,说出为什么。。。 ```go package main import ( "fmt" ) type People interface { Show() } type Student struct{} func (stu *Student) Show() { } func live() People { var stu *Student return stu } func main() { if live() == nil { fmt.Println("AAAAAAA") } else { fmt.Println("BBBBBBB") } } ``` **解析:** 跟上一题一样,不同的是`*Student` 的定义后本身没有初始化值,所以 `*Student` 是 `nil`的,但是`*Student` 实现了 `People` 接口,接口不为 `nil`。
相关推荐
golang 中解析 tag 是怎么实现的?反射原理是什么?(中高级肯定会问,比较难,需要自己多去总结)
使用gorm不当出现too Many Connections的问题
golang map 使用注意的点,是否并发安全?
uint 类型溢出问题
Golang中defer和return执行的先后顺序
golang orm框架 gorm
讲讲 Go 的 select 底层数据结构和一些特性?(难点,没有项目经常可能说不清,面试一般会问你项目中怎么使用select)
golang中两个变量值的4种交换方式
golang进行封包和拆包的完整解决方案
对已经关闭的 chan 进行读写,会怎么样?为什么?
调用函数传入结构体时,应该传值还是指针? (Golang 都是传值)
为 sync.WaitGroup 中Wait函数支持 WaitTimeout 功能.
单例模式的应用场景
Golang判断slice是否相等
Golang空结构体 struct{} 的使用
讲讲 Go 的 defer 底层数据结构和一些特性?
go defer,多个 defer 的顺序,defer 在什么时机会修改返回值?
序列化协议
Golang 单引号,双引号,反引号的区别?
Golang表示枚举类型的详细讲解
昨天那个在for循环里append元素的同事,今天还在么?
什么是死锁?死锁产生的原因?如何避免死锁?
golang并发题目测试
交替打印数字和字母
golang面试题
for range 的时候它的地址会发生变化么?
B+树为什么快
数组和切片的区别 (基本必问)
实现阻塞读且并发安全的map
机器人坐标问题
复利计算 递归/非递归
在 golang 协程和channel配合使用
MySQL索引原理
写出以下逻辑,要求每秒钟调用一次proc并保证程序不退出?
讲讲 Go 的 slice 底层数据结构和一些特性?
高并发下的锁与map的读写
Redis的优点
判断两个给定的字符串排序后是否一致
字符串替换问题
ElasticSearch使用场景
常见语法题目1
golang 中 make 和 new 的区别?(基本必问)
七道语法找错题目
判断字符串中字符是否全都不同
golang 实现一个负载均衡案例(随机,轮训)
redis缓存穿透、缓存击穿、缓存雪崩原因+解决方案
基本数据结构和算法
消息队列使用的场景介绍
操作系统基本原理
翻转字符串
redis在项目中的使用
TiDB使用场景
评论区
先去登录
版权所有:机遇屋在线 Copyright © 2021-2025 jiyuwu Co., Ltd.
鲁ICP备16042261号-1