本文绝大多数题目来源于网络,部分题目为原创。
slice相关
以下代码有什么问题,说明原因
1 | type student struct { |
每次遍历的时候stu变量为值拷贝,stu变量的地址未改变,即&stu未改变,遍历结束后stu指向stus中的最后一个元素。
使用reflect.TypeOf(str)
打印出的类型为main.student,如果使用stu.Age += 10
这样的语法是不会修改stus中的值的。
可修改为如下形式:
1 | for i, _ := range stus { |
有一个slice of object, 遍历slice修改name为指定的值
1 | type foo struct { |
意在考察range遍历的时候是值拷贝,以及slice的内部数据结构,slice的数据结构如下:
1 | struct Slice |
执行append函数后会返回一个新的Slice对象,新的Slice对象跟旧Slice对象共用相同的数据存储,但是len的值并不相同。
该题目中,可以通过下面的方式来修改值:
1 | // range方式 |
从slice中找到一个元素匹配name,并将该元素的指针添加到一个新的slice中,返回新slice
1 | func find(s []foo, name string) []*foo { |
仍旧是考察range是值拷贝的用法,此处使用for i 循环即可
1 | func find(s []foo, name string) []*foo { |
下面输出什么内容
1 | package main |
slice的函数传递为值拷贝方式,在函数m中对下标为0的元素的修改会直接修改原slice中的值,因为slice中的指针指向的地址是相同的。
append之后的slice虽然可能是在原数组上增加了元素,但原slice中的len字段并没有变化。
make([]int, 3, 6)虽然指定了slice的cap,但对于append没有影响,还是会在slice中最后一个元素的下一个位置增加新元素。
数组由于是值拷贝,对新数组的修改不会影响到原数组。
输出内容如下:
1 | [-1 2 3] |
下面输出什么内容
该题目为我自己想出来的,非来自于互联网,意在考察对slice和append函数的理解。
1 | func f() { |
输出结果如下,在执行第二个append后,第一个append在内存中增加的元素4会被5覆盖掉。执行结果可以通过fmt.Println(s1, cap(s1), &s1[0])
的形式将第一个元素的内存地址打印出来查看。
1 | [0 0 4] |
goroutine
以下代码输出内容:
1 | package main |
不会有任何输出
下面输出的内容
1 | type People struct{} |
输出
1 | showA |
有点出乎意料,可以举个反例,如果ShowA()方法会调用到Teacher类型的ShowB()方法,假设People和Teacher并不在同一个包中时,编译一定会出现错误。
Go中没有继承机制,只有组合机制。
下面代码会触发异常吗?请详细说明
1 | func main() { |
会间歇性触发异常,select会随机选择。
以下代码能编译过去吗?为什么?
1 | package main |
不能编译过去,提示Stduent does not implement People (Speak method has pointer receiver)
,将Speak定义更改为func (stu Stduent) Speak(think string) (talk string)
即可编译通过。
main的调用方式更改为如下也可以编译通过var peo People = new(Stduent)
。
func (stu *Stduent) Speak(think string) (talk string)
是*Student
类型的方法,不是Stduent
类型的方法。
下面输出什么
1 | package main |
一直输出9999.涉及到goroutine的切换时机,仅系统调用或者有函数调用的情况下才会切换goroutine,for循环情况下一直没有系统调用或函数切换发生,需要等到for循环结束后才会启动新的goroutine。
以下代码打印出来什么内容,说出为什么。。。
1 | package main |
打印BBBBBBB
。
byte与rune的关系
- byte alias for uint8
- rune alias for uint32,用来表示unicode
1 | func main() { |
写出打印的结果
1 | type People struct { |
打印结果为people: {123}
下面函数有什么问题?
1 | func funcMui(x,y int)(sum int,error){ |
函数返回值命名 在函数有多个返回值时,只要有一个返回值有指定命名,其他的也必须有命名。 如果返回值有有多个返回值必须加上括号; 如果只有一个返回值并且有命名也需要加上括号; 此处函数第一个返回值有sum名称,第二个为命名,所以错误。
以下函数输出什么
1 | package main |
输出结果为: 4 1 3 2
return语句不是一个原子指令,分为两个阶段,执行return后面的表达式和返回表达式的结果。defer函数在返回表达式之前执行。
- 执行return后的表达式给返回值赋值
- 调用defer函数
- 空的return
DeferFunc1在第一步执行表达式后t=1,执行defer后t=4,返回值为4
DeferFunc2在第一步执行表达式后t=1,执行defer后t=4,返回值为第一步表达式的结果1
DeferFunc3在第一步表达式为t=2,执行defer后t=3,返回值为t=3
DeferFunc4在第一步执行表达式后t=2,返回值为t=2
是否可以编译通过?如果通过,输出什么?
1 | package main |
结构体比较 进行结构体比较时候,只有相同类型的结构体才可以比较,结构体是否相同不但与属性类型个数有关,还与属性顺序相关。
还有一点需要注意的是结构体是相同的,但是结构体属性中有不可以比较的类型,如map,slice。 如果该结构属性都是可以比较的,那么就可以使用“==”进行比较操作。
是否可以编译通过?如果通过,输出什么?
1 | package main |
输出“non-empty interface”
交替打印数字和字母
使用两个 goroutine 交替打印序列,一个 goroutine 打印数字, 另外一个 goroutine 打印字母, 最终效果为: 12AB34CD56EF78GH910IJ1112KL1314MN1516OP1718QR1920ST2122UV2324WX2526YZ2728
1 | package main |
struct类型的方法调用
假设T类型的方法上接收器既有T类型的,又有T指针类型的,那么就不可以在不能寻址的T值上调用T接收器的方法。
请看代码,试问能正常编译通过吗?
1 | import ( |
能正常编译通过,并输出”poniter”
请接着看以下的代码,试问能编译通过?
1 | import ( |
不能编译通过。
“cannot call pointer method on Lili literal”
“cannot take the address of Lili literal”
其实在第一个代码示例中,main主函数中的“li”是一个变量,li的虽然是类型Lili,但是li是可以寻址的,&li的类型是Lili,因此可以调用Lili的方法。
golang context包的用法
- goroutine之间的传值
- goroutine之间的控制
在单核cpu的情况下,下面输出什么内容?
1 | package main |
考察golang的runtime机制,goroutine的切换时机只有在有系统调用或者函数调用时才会发生,本例子中的for循环结束之前不会发生goroutine的切换,所以最终输出结果为5.
下面输出什么
1 | package main |
编译不通过,仅*Student
实现了People接口,更改为var peo People = &Student{}
即可编译通过。
下面输出什么
1 | package main |
编译失败,常量cl通常在预处理阶段会直接展开,无法取其地址。
以下代码是否存在问题,请解释你的判断和理由
1 | import "sync" |
Mutex对象不能被值拷贝,后续传递需要使用指针的形式
以下代码输出是什么 解释一下
1 | func main() { |
本题意在考察string和slice的数据结构,string的数据结构如下:
case1的内存结构变化情况如下:
case2由于s1默认长度为0,直接使用s1[0]复制会出现panic错误。