常见语法题目 二
常见语法题目 二
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
关键字的实现跟go关键字很类似,不同的是它调用的是runtime.deferproc
而不是runtime.newproc
。
在defer
出现的地方,插入了指令call runtime.deferproc
,然后在函数返回之前的地方,插入指令call runtime.deferreturn
。
goroutine的控制结构中,有一张表记录defer
,调用runtime.deferproc
时会将需要defer的表达式记录在表中,而在调用runtime.deferreturn
的时候,则会依次从defer表中出栈并执行。
因此,题目最后输出顺序应该是defer
定义顺序的倒序。panic
错误并不能终止 defer
的执行。
2、 以下代码有什么问题,说明原因
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、下面的代码会输出什么,并说明原因
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.
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、下面代码会输出什么?
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、下面代码会触发异常吗?请详细说明
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、下面代码输出什么?
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、请写出以下输入内容
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、下面的代码有什么问题?
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、下面的迭代会有什么问题?
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、以下代码能编译过去吗?为什么?
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、以下代码打印出来什么内容,说出为什么。。。
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
。