本文共 11335 字,大约阅读时间需要 37 分钟。
对里面的部分题目做一些笔记备查。
33. 【中级】 golang中的引用类型包括()
A. 数组切片 B. map C. channel D. interface 参考答案:ABCD 这道题已经过时了,在2013年4月3日的github提交中已经明确说了。而slice源码的说明也由“引用”改成了“描述符”。Go只有值传递,没有所谓的引用传递。上题应该改为哪些类型包含有指针。34. 【中级】 golang中的指针运算包括()
A. 可以对指针进行自增或自减运算 B. 可以通过“&”取指针的地址 C. 可以通过“*”取指针指向的数据 D. 可以对指针进行下标运算 参考答案:BCD选项应该也是对的,在Go13.8里指向数组的指针的下标运算是可以支持的:
package main import "fmt" func main() { var a = [3]int{998, 999, 1000} var ip *[3]int = &a for i := 0; i < len(a); i++ { fmt.Printf("ip[%d]=%d\n", i, ip[i]) }}
输出:
ip[0]=998ip[1]=999ip[2]=1000
36. 【中级】下面赋值正确的是()
A. var x = nil B. var x interface{} = nil C. var x string = nil D. var x error = nil 参考答案:BD可以赋值成nil的变量类型有7种:1)任意类型的指针变量,2)函数变量,3)接口,4)error,5)map, 6)切片 7)通道。这些变量默认值就是nil,实际上都不用赋值成nil。
package mainimport "fmt"func main() { var ptrI *int var f func() var iface interface{} var err error var m map[string]string var sl []string var ch chan int if ptrI == nil {fmt.Printf("1) pointer is nil\n")} if f == nil {fmt.Printf("2) func is nil\n")} if iface == nil {fmt.Printf("3) interface is nil\n")} if err == nil {fmt.Printf("4) error is nil\n")} if m == nil {fmt.Printf("5) map is nil\n")} if sl == nil {fmt.Printf("6) slice is nil\n")} if ch == nil {fmt.Printf("7) ch is nil\n")}}
输出:
1) pointer is nil2) func is nil3) interface is nil4) error is nil5) map is nil6) slice is nil7) ch is nil
不能赋值成nil的变量类型,会在编译时报错:
package mainimport "fmt"type Person struct { name string}func main() { var str string = nil var person Person = nil var i int = nil var b bool = nil var x = nil fmt.Printf("str=%v person=%v i=%v b=%v x=%v", str, person, i, b, x)}
编译报错:
# command-line-arguments./tryinit.go:10:6: cannot use nil as type string in assignment./tryinit.go:11:6: cannot use nil as type Person in assignment./tryinit.go:12:6: cannot use nil as type int in assignment./tryinit.go:13:6: use of untyped nil
38. 【中级】从切片中删除一个元素,下面的算法实现正确的是()
A. func (s *Slice)Remove(value interface{})error { for i, v := range *s { if isEqual(value, v) { if i== len(*s) - 1 { *s = (*s)[:i] }else { *s = append((*s)[:i],(*s)[i + 2:]...) } return nil } } return ERR_ELEM_NT_EXIST }B.
func (s*Slice)Remove(value interface{}) error { for i, v:= range *s { if isEqual(value, v) { *s =append((*s)[:i],(*s)[i + 1:]) return nil } } returnERR_ELEM_NT_EXIST }C.
func (s*Slice)Remove(value interface{}) error { for i, v:= range *s { if isEqual(value, v) { delete(*s, v) return nil } } returnERR_ELEM_NT_EXIST }D.
func (s*Slice)Remove(value interface{}) error { for i, v:= range *s { if isEqual(value, v) { *s =append((*s)[:i],(*s)[i + 1:]...) return nil } } returnERR_ELEM_NT_EXIST }参考答案:D
A,切片截取是前闭后开;B,append追加另一个切片时需要加"...";C,delete是map的删除方式51. 【初级】对于局部变量整型切片x的赋值,下面定义正确的是()
A. x := []int{
1, 2, 3, 4, 5, 6, } B. x :=[]int{ 1, 2, 3, 4, 5, 6 } C. x :=[]int{ 1, 2, 3, 4, 5, 6} D. x :=[]int{1, 2, 3, 4, 5, 6,}参考答案:ACD
切片赋值时,最后一个元素后面如果有逗号则“}”可以换行或者不换行,否则”}“必须和最后一个元素保持在同一行。
55. 【初级】关于变量的自增和自减操作,下面语句正确的是()
A. i := 1;i++
B. i := 1;j = i++ C. i := 1;++i D. i := 1;i--参考答案:AD
Go目前只支持后置自增自减运算符,而且不能用于赋值或者作为参数传递给函数。
package mainimport "fmt"func main() { i:=1;i-- j := i++ //错误 if i++ > 0 { //错误 fmt.Printf("i=%v\n", i++) //错误 } ++i //错误 fmt.Printf("i=%v j=%v\n", i, j)}
编译报错:
# command-line-arguments./example.go:7:8: syntax error: unexpected ++ at end of statement./example.go:8:9: syntax error: unexpected >, expecting {./example.go:9:25: syntax error: unexpected ++, expecting comma or )./example.go:11:2: syntax error: unexpected ++, expecting }
57. 【中级】关于函数声明,下面语法错误的是()
A. func f(a, b int) (value int, err error) B. func f(a int, b int) (value int, err error) C. func f(a, b int) (value int, error) D. func f(a int, b int) (int, int, error)参考答案:C
Go语言的函数返回值,要么全是非命名的返回值,要么全是命名的返回值,不能即有非命名的又有命名的返回值,否则编译会报错:syntax error: mixed named and unnamed function parameters
58. 【中级】如果Add函数的调用代码为:
func main() {
var a Integer = 1 var b Integer = 2 var i interface{} = &a sum := i.(*Integer).Add(b) fmt.Println(sum) } 则Add函数定义正确的是() A. type Integer int func (a Integer) Add(b Integer) Integer { return a + b } B type Integer int func (a Integer) Add(b *Integer) Integer { return a + *b } C type Integer int func (a *Integer) Add(b Integer) Integer { return *a + b } D type Integer int func (a *Integer) Add(b *Integer) Integer { return *a + *b } 参考答案:AC 对于方法的参数而言,指针类型的参数不能接受值类型的参数,反之亦然,所以BD是错的。而对于结构体的方法而言,不论定义成值方法或者指针方法,都可以同时支持用指针或者非指针实例来调用。(可能是在编译期完成的转换,后续还需要确认)有两种特殊情况只能定义成值方法:一是接口断言成非指针实例,例如把main函数改为:
func main() { var a Integer = 1 var b Integer = 2 var c interface{} = a sum := c.(Integer).Add(b) fmt.Println(sum)}
则答案是 A
运行C则编译报错:./example.go:9:20: cannot call pointer method on c.(Integer)./example.go:9:20: cannot take the address of c.(Integer)
二是类型强制转换,例如把main函数改为:
func main() { var a int = 1 var b Integer = 2 sum := Integer(a).Add(b) fmt.Println(sum)}
则答案是 A
运行C则同样是编译报错:./example.go:8:19: cannot call pointer method on Integer(a)./example.go:8:19: cannot take the address of Integer(a)
72. 【中级】关于GetPodAction定义,下面赋值正确的是()
type Fragment interface { Exec(transInfo *TransInfo) error } type GetPodAction struct { } func (g GetPodAction) Exec(transInfo *TransInfo) error { return nil } A. var fragment Fragment =new(GetPodAction) B. var fragment Fragment = GetPodAction C. var fragment Fragment = &GetPodAction{} D. var fragment Fragment = GetPodAction{} 参考答案:ACD Fragment是接口所以赋值成指针或者非指针,AC是指针,D是非指针。B编译报错“type GetPodAction is not an expression” 结构体创建实例有两种等价的形式: 1) var g GetPodAction; 2) g := GetPodAction{}82. 【中级】关于接口,下面说法正确的是()
A. 只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等价的,可以相互赋值 B. 如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A C. 接口查询是否成功,要在运行期才能够确定 D. 接口赋值是否可行,要在运行期才能够确定参考答案:ABC
接口赋值时编译期就可以报错。 验证代码:package mainimport "fmt"type A interface { f1() f2() f3()}type B interface { f2() f1()}type AImpl struct {}func (a AImpl) f1() { fmt.Printf("call AImpl f1()\n")}func (a AImpl) f2() { fmt.Printf("call AImpl f2()\n")}func (a AImpl) f3() { fmt.Printf("call AImpl f3()\n")}func main() { var a A = AImpl{} var b B = a b.f1() //接口查询,判断b是否实现了接口A if c, ok := b.(A); ok { c.f3() }}
输出:
call AImpl f1()call AImpl f3()
83. 【初级】关于channel,下面语法正确的是()
A. var ch chan int B. ch := make(chan int) C. <- ch D. ch <- 参考答案:ABC85. 【中级】 golang中大多数数据类型都可以转化为有效的JSON文本,下面几种类型除外()
A. 指针 B. channel C. complex D. 函数 参考答案:BCD87. 【初级】 flag是bool型变量,下面if表达式符合编码规范的是()
A. if flag == 1 B. if flag C. if flag == false D. if !flag 参考答案:BD 这道题C也是符合规范的,Go的源代码有很多这样的写法,例如:runtime/pprof/internal/profile:func encodeBoolOpt(b *buffer, tag int, x bool) { if x == false { return } encodeBool(b, tag, x)}
88. 【初级】 value是整型变量,下面if表达式符合编码规范的是()
A. if value == 0 B. if value C. if value != 0 D. if !value 参考答案:AC BD两项只能是bool变量,否则编译报错:“non-bool value (type int) used as if condition”,“invalid operation: ! int”91. 【中级】关于slice或map操作,下面正确的是()
A. var s []int s = append(s,1) B. var m map[string]int m["one"] = 1 C. var s []int s = make([]int, 0) s = append(s,1)D.
var m map[string]int m = make(map[string]int) m["one"] = 1参考答案:ACD
slice和map在插入数据之前都需要分配内存,而函数内置有内存分配操作,所以在调用append之前可以不用make切片。93. 【中级】关于channel的特性,下面说法正确的是()
A. 给一个 nil channel 发送数据,造成永远阻塞 B. 从一个 nil channel 接收数据,造成永远阻塞 C. 给一个已经关闭的 channel 发送数据,引起 panic D. 从一个已经关闭的 channel 接收数据,如果缓冲区中为空,则返回一个零值参考答案:ABCD
这道题AB两个选项应该是适用于Go的早期版本,在Go1.13.8里面,AB直接报fatal error然后退出。C,D是正确的。 下面是一段能正常运行的代码:func main() { ch := make(chan bool) go func(ch chan bool) { time.Sleep(1 * time.Second) b := <-ch fmt.Printf("b=%v\n", b) }(ch) ch <- true fmt.Printf("exit\n")}
b=trueexit
按照AB说法,把"ch := make(chan bool)"改成:
func main() { var ch chan bool go func(ch chan bool) { time.Sleep(1 * time.Second) b := <-ch fmt.Printf("b=%v\n", b) }(ch) ch <- true fmt.Printf("exit\n")}
报错退出:
fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send (nil chan)]:main.main() /home/zhoupeng/go-test/src/tutorial/example.go:15 +0x5bgoroutine 6 [chan receive (nil chan)]:main.main.func1(0x0) /home/zhoupeng/go-test/src/tutorial/example.go:12 +0x4bcreated by main.main /home/zhoupeng/go-test/src/tutorial/example.go:10 +0x42exit status 2
验证C,在“ch <- true”之前加入“close(ch)”,则报错:“panic: send on closed channel”
func main() { ch := make(chan bool) go func(ch chan bool) { time.Sleep(1 * time.Second) b := <-ch fmt.Printf("b=%v\n", b) }(ch) close(ch) ch <- true fmt.Printf("exit\n")}
验证D,通道写入true之后关闭通道,等待3秒,协程等待1秒后连读2次通道,第一次读到缓冲区里的“true”,第二次缓冲区为空则读取到布尔变量的零值false。注,数值型变量的零值是0,字符串的零值是“”。
func main() { ch := make(chan bool, 1) go func(ch chan bool) { time.Sleep(1 * time.Second) b1 := <-ch fmt.Printf("b1=%v\n", b1) b2 := <-ch fmt.Printf("b2=%v\n", b2) }(ch) ch <- true close(ch) time.Sleep(3 * time.Second) fmt.Printf("exit\n")}
b1=trueb2=falseexit
94. 【中级】关于无缓冲和有冲突的channel,下面说法正确的是()
A. 无缓冲的channel是默认的缓冲为1的channel B. 无缓冲的channel和有缓冲的channel都是同步的 C. 无缓冲的channel和有缓冲的channel都是非同步的 D. 无缓冲的channel是同步的,而有缓冲的channel是非同步的参考答案:D
无缓冲的channel是默认的缓冲为0的channel,对应代码如“make(chan int, 0)”,0是默认值可以省略。有缓冲的channel是非同步的指的是发送/接收操作只在缓冲区满了才阻塞,否则继续执行后面的代码。 缓存为1的时序验证:func main() { ch := make(chan int, 1) go func(ch chan int) { time.Sleep(2 * time.Second) i1 := <-ch log.Printf("i1=%v\n", i1) time.Sleep(2 * time.Second) i2 := <-ch log.Printf("i2=%v\n", i2) }(ch) log.Printf("send 1\n") ch <- 1 log.Printf("send 2\n") ch <- 2 log.Printf("main sleep\n") time.Sleep(3 * time.Second) log.Printf("exit\n")}
2020/07/13 14:19:26 send 12020/07/13 14:19:26 send 22020/07/13 14:19:28 i1=12020/07/13 14:19:28 main sleep2020/07/13 14:19:30 i2=22020/07/13 14:19:31 exit
从输出可以看出,缓冲区是一个先进先出的队列,发送的顺序是1,2,接收的顺序也是1,2。运行到send2时缓冲区已满,main函数所在协程阻塞,2秒之后另一个协程从通道读取到第一个值1,缓冲区不再是已满的状态,main函数停止阻塞继续执行后面的代码打印“main sleep”,再过2秒之后另一个协程从通道读取到第二个值2。
如果把上述代码改为非缓冲通道,则输出时序为:2020/07/13 14:37:15 send 12020/07/13 14:37:17 i1=12020/07/13 14:37:17 send 22020/07/13 14:37:19 i2=22020/07/13 14:37:19 main sleep2020/07/13 14:37:22 exit
send1之后就会阻塞,2秒之后等另一个协程读取到第一个值1之后,main再发送send 2,发了之后再阻塞 ,2秒之后等另一个协程读取到第二个值2之后,main解除阻塞。
95. 【中级】关于异常的触发,下面说法正确的是()
A. 空指针解析 B. 下标越界 C. 除数为0 D. 调用panic函数 参考答案:ABCD96. 【中级】关于cap函数的适用类型,下面说法正确的是()
A. array B. slice C. map D. channel 参考答案:ABDfunc main(){ var a [3]int var sl = make([]int, 1, 5) var m = make(map[int]int, 100) var ch = make(chan int, 10) log.Printf("%d\n",cap(a)) log.Printf("%d\n",cap(sl)) log.Printf("%d\n",cap(m)) log.Printf("%d\n",cap(ch))}
编译报错:
invalid argument m (type map[int]int) for cap
100. 【中级】关于map,下面说法正确的是()
A. map反序列化时json.unmarshal的入参必须为map的地址 B. 在函数调用中传递map,则子函数中对map元素的增加不会导致父函数中map的修改 C. 在函数调用中传递map,则子函数中对map元素的修改不会导致父函数中map的修改 D. 不能使用内置函数delete删除map的元素参考答案:A
A:func Unmarshal(data [], v interface{}) 其中“If v is nil or not a pointer, Unmarshal returns an InvalidUnmarshalError.”所以v只能是非空指针。 B,C:子函数对map的"增,删,改"都会导致父函数中map的改变,验证代码如下:func test(mp map[int]int){ mp[23] = 321 mp[11] = 100 delete(mp, 12)}func main(){ var m = make(map[int]int) m[11] = 108 m[12] = 110 log.Printf("%v\n",m) test(m) log.Printf("%v\n",m)}
[root@dev tutorial]# go run example.go2020/07/14 10:18:49 map[11:108 12:110]2020/07/14 10:18:49 map[11:100 23:321]
102. 【初级】关于select机制,下面说法正确的是()
A. select机制用来处理异步IO问题 B. select机制最大的一条限制就是每个case语句里必须是一个IO操作 C. golang在语言级别支持select关键字 D. select关键字的用法与switch语句非常类似,后面要带判断条件参考答案:ABC
A 不准确,可以用来处理异步IO问题,也可以处理同步IO; B不对,每个case语句里必须是接收通道的操作,不一定非得是IO操作,golang并没有这样的硬性限制。C. 是对的,golang ”语言级别“ 支持select关键字,而不需要引用其它包。D.后面跟的是对通道的接收操作。103. 【初级】关于内存泄露,下面说法正确的是()
A. golang有自动垃圾回收,不存在内存泄露 B. golang中检测内存泄露主要依靠的是pprof包 C. 内存泄露可以在编译阶段发现 D. 应定期使用浏览器来查看系统的实时内存信息,及时发现内存泄露问题参考答案:BD
转载地址:http://lnmwn.baihongyu.com/