码农行者 码农行者
首页
  • Python

    • 语言特性
    • Django相关
    • Tornado
    • Celery
  • Golang

    • golang学习笔记
    • 对比python学习go
    • 模块学习
  • JavaScript

    • Javascript
  • 数据结构预算法笔记
  • ATS
  • Mongodb
  • Git
云原生
运维
垃圾佬的快乐
  • 数据库
  • 机器学习
  • 杂谈
  • 面试
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

DeanWu

软件工程师
首页
  • Python

    • 语言特性
    • Django相关
    • Tornado
    • Celery
  • Golang

    • golang学习笔记
    • 对比python学习go
    • 模块学习
  • JavaScript

    • Javascript
  • 数据结构预算法笔记
  • ATS
  • Mongodb
  • Git
云原生
运维
垃圾佬的快乐
  • 数据库
  • 机器学习
  • 杂谈
  • 面试
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 面试主观问题集锦
  • 面试技术问题
  • go面试题集锦
  • python面试题集锦
  • go理论题
  • go语法题二
  • go语法题一
    • 常见语法题目 一
      • 1、下面代码能运行吗?为什么。
      • 2、请说出下面代码存在什么问题。
      • 3、写出打印的结果。
      • 4、下面的代码是有问题的,请说明原因。
      • 5、请找出下面代码的问题所在。
      • 6、请说明下面代码书写是否正确。
      • 7、下面的程序运行后为什么会爆异常。
      • 8、请说出下面代码哪里写错了
      • 9、请说出下面代码,执行时为什么会报错
      • 10、请说出下面的代码存在什么问题?
      • 11、下面这段代码为什么会卡死?
  • 面试
DeanWu
2023-10-09
目录

go语法题一

# 常见语法题目 一

# 1、下面代码能运行吗?为什么。

type Param map[string]interface{}

type Show struct {
	Param
}

func main1() {
	s := new(Show)
	s.Param["RMB"] = 10000
}
1
2
3
4
5
6
7
8
9
10

解析

共发现两个问题:

  1. main 函数不能加数字。
  2. new 关键字无法初始化 Show 结构体中的 Param 属性,所以直接对 s.Param 操作会出错。

# 2、请说出下面代码存在什么问题。

type student struct {
	Name string
}

func zhoujielun(v interface{}) {
	switch msg := v.(type) {
	case *student, student:
		msg.Name
	}
}
1
2
3
4
5
6
7
8
9
10

解析:

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 (opens new window)

# 3、写出打印的结果。

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)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

解析:

按照 golang 的语法,小写开头的方法、属性或 struct 是私有的,同样,在json 解码或转码的时候也无法上线私有属性的转换。

题目中是无法正常得到People的name值的。而且,私有属性name也不应该加json的标签。

# 4、下面的代码是有问题的,请说明原因。

type People struct {
	Name string
}

func (p *People) String() string {
	return fmt.Sprintf("print: %v", p)
}

func main() {
 	p := &People{}
	p.String()
}
1
2
3
4
5
6
7
8
9
10
11
12

解析:

在golang中String() string 方法实际上是实现了String的接口的,该接口定义在fmt/print.go 中:

type Stringer interface {
	String() string
}
1
2
3

在使用 fmt 包中的打印方法时,如果类型实现了这个接口,会直接调用。而题目中打印 p 的时候会直接调用 p 实现的 String() 方法,然后就产生了循环调用。

# 5、请找出下面代码的问题所在。

func main() {
	ch := make(chan int, 1000)
	go func() {
		for i := 0; i < 10; i++ {
			ch <- i
		}
	}()
	go func() {
		for {
			a, ok := <-ch
			if !ok {
				fmt.Println("close")
				return
			}
			fmt.Println("a: ", a)
		}
	}()
	close(ch)
	fmt.Println("ok")
	time.Sleep(time.Second * 100)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

解析:

在 golang 中 goroutine 的调度时间是不确定的,在题目中,第一个写 channel 的 goroutine 可能还未调用,或已调用但没有写完时直接 close 管道,可能导致写失败,既然出现 panic 错误。

# 6、请说明下面代码书写是否正确。

var value int32

func SetValue(delta int32) {
	for {
		v := value
		if atomic.CompareAndSwapInt32(&value, v, (v+delta)) {
			break
		}
	}
}
1
2
3
4
5
6
7
8
9
10

解析:

atomic.CompareAndSwapInt32 函数不需要循环调用。

# 7、下面的程序运行后为什么会爆异常。

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()
}
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

解析:

有一下几个问题:

  1. time.Sleep 的参数数值太大,超过了 1<<63 - 1 的限制。
  2. defer p.deferError() 需要在协程开始出调用,否则无法捕获 panic。

# 8、请说出下面代码哪里写错了

func main() {
	abc := make(chan int, 1000)
	for i := 0; i < 10; i++ {
		abc <- i
	}
	go func() {
		for  a := range abc  {
			fmt.Println("a: ", a)
		}
	}()
	close(abc)
	fmt.Println("close")
	time.Sleep(time.Second * 100)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

解析:

协程可能还未启动,管道就关闭了。

# 9、请说出下面代码,执行时为什么会报错

type Student struct {
	name string
}

func main() {
	m := map[string]Student{"people": {"zhoujielun"}}
	m["people"].name = "wuyanzu"
}

1
2
3
4
5
6
7
8
9

解析:

map的value本身是不可寻址的,因为map中的值会在内存中移动,并且旧的指针地址在map改变时会变得无效。故如果需要修改map值,可以将map中的非指针类型value,修改为指针类型,比如使用map[string]*Student.

# 10、请说出下面的代码存在什么问题?

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)
}
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

解析:

依据4个goroutine的启动后执行效率,很可能打印111func4,但其他的111func*也可能先执行,exec只会返回一条信息。

# 11、下面这段代码为什么会卡死?

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
11
12
13
14
15
16
17
18
19

解析:

Golang 中,byte 其实被 alias 到 uint8 上了。所以上面的 for 循环会始终成立,因为 i++ 到 i=255 的时候会溢出,i <= 255 一定成立。

也即是, for 循环永远无法退出,所以上面的代码其实可以等价于这样:

go func() {
    for {}
}
1
2
3

正在被执行的 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() 等待所有协程退出。

go语法题二

← go语法题二

最近更新
01
chromebox/chromebook 刷bios步骤
03-01
02
redis 集群介绍
11-28
03
go语法题二
10-09
更多文章>
Theme by Vdoing | Copyright © 2015-2024 DeanWu | 遵循CC 4.0 BY-SA版权协议
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式