下面的代码输出什么?
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
}
考点:defer执行顺序
解答:
这道题类似第一题,需要注意到defer执行顺序和值传递index:1肯定是最后执行的,但是index:1的第三个参数是一个函数,所以最先被调用
calc(“10”, 1, 2) = => 10, 1, 2, 3
执行到index:2时,与之前一样,需要先调用calc(“20”, 0, 2) = => 20, 0, 2, 2
执行到b = 1时候开始调用,index:2 = => calc(“2”, 0, 2) = => 2, 0, 2, 2
最后执行index:1 = => calc(“1”, 1, 3) = => 1, 1, 3, 4
10 1 2 3
20 0 2 2
2 0 2 2
1 1 3 4
请写出以下输入内容
func main() {
s := make([]int, 5)
s = append(s, 1, 2, 3)
fmt.Println(s)
}
考点:make默认值和append
解答:
make初始化是有默认值的,此处默认值为0
[0 0 0 0 0 1 2 3]
下面的代码有什么问题
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
}
考点:map线程安全
解答:
可能会出现fatel error: concurrent mapreadandmapwrite.
修改一下看看效果
func (ua *UserAges) Get(name string) int {
ua.Lock()
defer ua.Unlock()
if age, ok := ua.ages[name];ok {
return age
}
return -1
}
下面的迭代会有什么问题?
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
}
考点:chan缓存池
解答:
看到这道题目,我也在猜想出题者的意图在哪里。chan?sync.RWMutex?go?chan缓存池?迭代?所以只能再读一次题目,就从迭代入手看看,既然是迭代就会要求ste.s全部可以遍历一次。但是chan是为缓存的,那就代表写入一次就会阻塞。我们把代码恢复为可以运行的方式,看看效果
package main
import (
"sync"
"fmt"
)
type threadSafeSet struct {
sync.RWMutex
s []interface{}
}
func (set *threadSafeSet) Iter() <-chan interface{} {
// ch := make(chan interface{})
ch := make(chan interface{}, len(set.s))
go func() {
set.RLock()
for elem, value := range set.s {
ch <- elem
fmt.Println("Iter:", elem, value)
}
close(ch)
set.RUnlock()
}()
return ch
}
func main() {
th := threadSafeSet{
s:[]interface{}{"1", "2"},
}
v := <-th.Iter()
fmt.Sprintf("%s%v", "ch", v)
}
以下代码能编译过去吗?为什么?
package main
import "fmt"
type People interface {
Speak(string) string
}
type Student struct {}
func (stu *Student) Speak(think stirng) (talk string) {
if think == "bitch" {
taik = "you are a good boy"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = Student{}
think := "bitch"
fmt.Println(peo.Speak(think))
}
考点:golang的方法集
解答:
编译不通过!做错了!?说明你对golang的方法集还有一些疑问。一句话:golang的方法集仅仅影响接口实现和方法表达式转化,与通过实例或者指针调用方法无关。
以下代码打印出来什么内容,说出为什么
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")
}
}
解答:interface内部结构
解答:
很经典的题!这个考点是很多人忽略的interface内部结构。go中的接口分为两种,一种是空接口,类似这样的:var in interface{}
,另一种如题目的:type People interface {Show()}
他们的地层结构如下
// 空接口
type eface struct {
_type *_type //类型信息
data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
}
// 带有方法的接口
type iface struct {
tab *itab //存储type信息还有结构实现方法的集合
data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
}
type _type struct {
size uintptr //类型大小
ptrdata uintptr //前缀持有所有指针的内存大小
hash uint32 //数据hash值
tflag tflag
align uint8 //对齐
fieldalign unit8 //嵌入结构体时的对齐
kind uint8 //kind 有些枚举值kind等于0是无效的
alg *typeAlg //函数指针数组,类型实现的所有方法
gcdata *byte
str nameOff
ptrToThis typeOff
}
type itab struct {
inter *interfacetype //接口类型
_type *_type
link *itab
bad int32
inhash int32
fun [1]uintptr //可变大小方法集合
}
可以看出iface比eface中间多了一层itab结构。itab存储_type信息和[]fun方法集,从上面的结构我们就可得出,因为data指向了nil并不代表interface是nil,所以返回值并不为空,这里的fun(方法集)定义了接口的接收规则,在编译过程中需要验证是否实现接口
结果:BBBBBBB