常见语法题目 二

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、 以下代码有什么问题,说明原因

解析:

golang 的 for ... range 语法中,stu 变量会被复用,每次循环会将集合中的值复制给这个变量,因此,会导致最后m中的map中储存的都是stus最后一个student的值。

3、下面的代码会输出什么,并说明原因

解析:

这个输出结果决定来自于调度器优先调度哪个G。从runtime的源码可以看到,当创建一个G时,会优先放入到下一个调度的runnext字段上作为下一次优先调度的G。因此,最先输出的是最后创建的G,也就是9.

4、下面代码会输出什么?

解析:

输出结果为showAshowB。golang 语言中没有继承概念,只有组合,也没有虚方法,更没有重载。因此,*TeacherShowB 不会覆写被组合的 People 的方法。

5、下面代码会触发异常吗?请详细说明

解析:

结果是随机执行。golang 在多个case 可读的时候会公平的选中一个执行。

6、下面代码输出什么?

解析:

输出结果为:

defer 在定义的时候会计算好调用函数的参数,所以会优先输出1020 两个参数。然后根据定义的顺序倒序执行。

7、请写出以下输入内容

解析:

输出为 0 0 0 0 0 1 2 3

make 在初始化切片时指定了长度,所以追加数据时会从len(s) 位置开始填充数据。

8、下面的代码有什么问题?

解析:

在执行 Get方法时可能被panic。

虽然有使用sync.Mutex做写锁,但是map是并发读写不安全的。map属于引用类型,并发读写时多个协程见是通过指针访问同一个地址,即访问共享变量,此时同时读写资源存在竞争关系。会报错误信息:“fatal error: concurrent map read and map write”。

因此,在 Get 中也需要加锁,因为这里只是读,建议使用读写锁 sync.RWMutex

9、下面的迭代会有什么问题?

解析:

默认情况下 make 初始化的 channel 是无缓冲的,也就是在迭代写时会阻塞。

10、以下代码能编译过去吗?为什么?

解析:

编译失败,值类型 Student{} 未实现接口People的方法,不能定义为 People类型。

在 golang 语言中,Student*Student 是两种类型,第一个是表示 Student 本身,第二个是指向 Student 的指针。

11、以下代码打印出来什么内容,说出为什么。。。

解析:

跟上一题一样,不同的是*Student 的定义后本身没有初始化值,所以 *Studentnil的,但是*Student 实现了 People 接口,接口不为 nil