常见语法题目 一
1、下面代码能运行吗?为什么。
1
2
3
4
5
6
7
8
9
10
type Param map[string]interface{}
type Show struct {
Param
}
func main1() {
s := new(Show)
s.Param["RMB"] = 10000
}
解析
共发现两个问题:
main
函数不能加数字。new
关键字无法初始化Show
结构体中的Param
属性,所以直接对s.Param
操作会出错。
2、请说出下面代码存在什么问题。
1
2
3
4
5
6
7
8
9
10
type student struct {
Name string
}
func t(v interface{}) {
switch msg := v.(type) {
case *student, student:
msg.Name
}
}
解析:
golang中有规定,switch type
的case T1
,类型列表只有一个,那么v := m.(type)
中的v
的类型就是T1类型。
如果是case T1, T2
,类型列表中有多个,那v
的类型还是多对应接口的类型,也就是m
的类型。
所以这里msg
的类型还是interface{}
,所以他没有Name
这个字段,编译阶段就会报错。具体解释见: https://golang.org/ref/spec#Type_switches
3、写出打印的结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type People struct {
name string `json:"name"`
}
func main() {
js := `{
"name":"11"
}`
var p People
err := json.Unmarshal([]byte(js), &p)
if err != nil {
fmt.Println("err: ", err)
return
}
fmt.Println("people: ", p)
}
解析:
按照 golang 的语法,小写开头的方法、属性或 struct
是私有的,同样,在json
解码或转码的时候也无法上线私有属性的转换。
题目中是无法正常得到People
的name
值的。而且,私有属性name
也不应该加json
的标签。
6、请说明下面代码书写是否正确
1
2
3
4
5
6
7
8
9
10
var value int32
func SetValue(delta int32) {
for {
v := value
if atomic.CompareAndSwapInt32(&value, v, (v+delta)) {
break
}
}
}
解析:
atomic.CompareAndSwapInt32
函数不需要循环调用。
7、下面的程序运行后为什么会爆异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
type Project struct{}
func (p *Project) deferError() {
if err := recover(); err != nil {
fmt.Println("recover: ", err)
}
}
func (p *Project) exec(msgchan chan interface{}) {
for msg := range msgchan {
m := msg.(int)
fmt.Println("msg: ", m)
}
}
func (p *Project) run(msgchan chan interface{}) {
for {
defer p.deferError()
go p.exec(msgchan)
time.Sleep(time.Second * 2)
}
}
func (p *Project) Main() {
a := make(chan interface{}, 100)
go p.run(a)
go func() {
for {
a <- "1"
time.Sleep(time.Second)
}
}()
time.Sleep(time.Second * 100000000000000)
}
func main() {
p := new(Project)
p.Main()
}
解析:
有一下几个问题:
time.Sleep
的参数数值太大,超过了1<<63 - 1
的限制。defer p.deferError()
需要在协程开始出调用,否则无法捕获panic
。
9、请说出下面代码,执行时为什么会报错
1
2
3
4
5
6
7
8
9
type Student struct {
name string
}
func main() {
m := map[string]Student{"people": {"ttt"}}
m["people"].name = "wuyanzu"
}
解析:
map
中value
是非指针类型,可以当做一个原子变量,只能统一更改,不能只改Student
其中一个成员,如果想要更改Student
其中一个成员,使用map[string]*Student
.
10、请说出下面的代码存在什么问题?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
type query func(string) string
func exec(name string, vs ...query) string {
ch := make(chan string)
fn := func(i int) {
ch <- vs[i](name)
}
for i, _ := range vs {
go fn(i)
}
return <-ch
}
func main() {
ret := exec("111", func(n string) string {
return n + "func1"
}, func(n string) string {
return n + "func2"
}, func(n string) string {
return n + "func3"
}, func(n string) string {
return n + "func4"
})
fmt.Println(ret)
}
解析:
依据4个goroutine的启动后执行效率,很可能打印111func4,但其他的111func*也可能先执行,exec只会返回一条信息。
但是函数exec在启动的goroutine执行完前就返回了,故,会存在ch挂起的问题
11、下面这段代码为什么会卡死?
`go package main
import ( “fmt” “runtime” )
func main() { var i byte go func() { for i = 0; i <= 255; i++ { } }() fmt.Println(“Dropping mic”) // Yield execution to force executing other goroutines runtime.Gosched() runtime.GC() fmt.Println(“Done”) }
1
2
3
4
5
6
7
8
9
10
**解析:**
Golang 中,byte 其实被 alias 到 uint8 上了。所以上面的 for 循环会始终成立,因为 i++ 到 i=255 的时候会溢出,i <= 255 一定成立。
也即是, for 循环永远无法退出,所以上面的代码其实可以等价于这样:
```go
go func() {
for {}
}
正在被执行的 goroutine 发生以下情况时让出当前 goroutine 的执行权,并调度后面的 goroutine 执行:
- IO 操作
- Channel 阻塞
- system call
- 运行较长时间
如果一个 goroutine 执行时间太长,scheduler 会在其 G 对象上打上一个标志( preempt),当这个 goroutine 内部发生函数调用的时候,会先主动检查这个标志,如果为 true 则会让出执行权。
main 函数里启动的 goroutine 其实是一个没有 IO 阻塞、没有 Channel 阻塞、没有 system call、没有函数调用的死循环。
也就是,它无法主动让出自己的执行权,即使已经执行很长时间,scheduler 已经标志了 preempt。
而 golang 的 GC 动作是需要所有正在运行 goroutine
都停止后进行的。因此,程序会卡在 runtime.GC()
等待所有协程退出。
写出以下代码出现的问题
1
2
3
4
5
6
7
8
9
10
11
package main
import (
"fmt"
)
func main() {
var x string = nil
if x == nil {
x = "default"
}
fmt.Println(x)
}
golang 中字符串是不能赋值 nil
的,也不能跟 nil
比较。
写出以下打印内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import "fmt"
const (
a = iota
b = iota
)
const (
name = "menglu"
c = iota
d = iota
)
func main() {
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
fmt.Println(d)
}
1
0 1 1 2
写出以下打印结果,并解释下为什么这么打印的。
1
2
3
4
5
6
7
8
9
10
11
12
package main
import (
"fmt"
)
func main() {
str1 := []string{"a", "b", "c"}
str2 := str1[1:]
str2[1] = "new"
fmt.Println(str1)
str2 = append(str2, "z", "x", "y")
fmt.Println(str1)
}
golang 中的切片底层其实使用的是数组。当使用str1[1:]
使,str2
和 str1
底层共享一个数组,这回导致 str2[1] = "new"
语句影响 str1
。
而 append
会导致底层数组扩容,生成新的数组,因此追加数据后的 str2
不会影响 str1
。
但是为什么对 str2
复制后影响的确实 str1
的第三个元素呢?这是因为切片 str2
是从数组的第二个元素开始,str2
索引为 1 的元素对应的是 str1
索引为 2 的元素。
写出以下打印结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
"fmt"
)
type Student struct {
Name string
}
func main() {
fmt.Println(&Student{Name: "menglu"} == &Student{Name: "menglu"})
fmt.Println(Student{Name: "menglu"} == Student{Name: "menglu"})
}
指针类型比较的是指针地址,非指针类型比较的是每个属性的值。
1
2
false
true
写出以下代码的问题
1
2
3
4
5
6
7
8
9
10
package main
import (
"fmt"
)
func main() {
fmt.Println([...]string{"1"} == [...]string{"1"})
fmt.Println([]string{"1"} == []string{"1"})
}
slice只能与nil进行比较。