• Go常见的语法题目


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

    type Param map[string]interface{}
    
    type Show struct {
    	Param
    }
    
    func main1() {
    	s := new(Show)
    	s.Param["RMB"] = 10000
    }
    

    解析

    共发现两个问题:

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

    解析:

    golang中有规定,switch typecase T1,类型列表只有一个,那么v := m.(type)中的v的类型就是T1类型。

    如果是case T1, T2,类型列表中有多个,那v的类型还是多对应接口的类型,也就是m的类型。

    所以这里msg的类型还是interface{},所以他没有Name这个字段,编译阶段就会报错。具体解释见: https://golang.org/ref/spec#Type_switches

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

    解析:

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

    题目中是无法正常得到Peoplename值的。而且,私有属性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()
    }
    

    解析:

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

    type Stringer interface {
    	String() string
    }
    

    在使用 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)
    }
    

    解析:

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

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

    var value int32
    
    func SetValue(delta int32) {
    	for {
    		v := value
    		if atomic.CompareAndSwapInt32(&value, v, (v+delta)) {
    			break
    		}
    	}
    }
    

    解析:

    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. 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 := <-abc
    			fmt.Println("a: ", a)
    		}
    	}()
    	close(abc)
    	fmt.Println("close")
    	time.Sleep(time.Second * 100)
    }
    

    解析:

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

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

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

    解析:

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

    解析:

    依据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")
    }
    

    解析:

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

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

    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() 等待所有协程退出。

  • 相关阅读:
    Git上传新项目
    斩波稳定(自稳零)精密运算放大器
    二、T100应收管理之订金立帐
    MySQL数据库——存储引擎(2)-存储引擎特点(InnoDB、MyISAM、Memory)、存储引擎选择
    要学习使用 calib3D 模块在图像中创建 3D 效果-姿势估计
    android应用后台常驻内存优化(二) 类重命名
    XC5VLX30T-2FF323I Virtex-5 LXT FPGA IC 产品参数
    Linux C编译器从零开发二
    Delphi7 提示can‘t load package *.bpl 错误问题的解决方法
    网络协议--TCP的成块数据流
  • 原文地址:https://blog.csdn.net/linzhongshu/article/details/139339141