在面试的过程中, 如果恰好遇到对方日常也使用 Go 做为主力语言, 我会选择一些简单而可扩展的问题交流下双方对 Go 的熟悉程度.
我喜欢的一个问题是让面试者告诉我下述代码的运行结果:
func main() {
for i := 0; i < 3; i++ {
go func() {
fmt.Println(i)
}()
}
time.Sleep(time.Second)
}
正确的答案应该是: 乱序输出三个数字. 对于三种错误答案: 输出 1, 2, 3; 输出三个数字; 乱序输出 1, 2, 3; 都可以通过反问再给予一次机会.
进一步的, 我们可以询问如何让其至少将 1, 2, 3 都输出一次.
大多数时候, 我们的得到的答案会是将 i 做为参数传入.
此时我喜欢再追问, 下述代码中 i := i
的写法是否正确.
func main() {
for i := 0; i < 3; i++ {
i := i
go func() {
fmt.Println(i)
}()
}
time.Sleep(time.Second)
}
我并不认为这是一个 Language Lawyer 问题, 由于 Go 中 for 循环的特殊实现方式,
i := i
这种方式在 Go 中是普遍存在的.
极少数情况下, 我们可以再讨论下上述例子的原因, 允许面试者有更大的发挥机会. 其中包括的点有:
我们在下述例子中看到, i 和 v 的内存地址始终未曾改变:
~ cat main.go
func main() {
nums := []int{1, 2, 3}
for i, v := range nums {
fmt.Println(&i, &v)
}
}
~ go run main.go
0x1400009a018 0x1400009a020
0x1400009a018 0x1400009a020
0x1400009a018 0x1400009a020
~ cat main.go | grep -A 7 "func fnVarScope"
func fnVarScope() {
s := "hello world"
{
s := 10
fmt.Println("s:", s)
}
fmt.Println("s:", s)
}
~ go run main.go
s: 10
s: hello world
1
lesismal 356 天前 10
个人觉得研究这些细节挺好玩,但是卷到面试题里真挺烦的
像我们很多务实的人喜欢按简单正确的方式写,不喜欢语法上的茴字的 N 种写法的那些奇技淫巧,所以除了手误、正常情况下不会在写 for lopp i 里再写个 i:=i ,即使要临时变量复制也是 idx:=i 或者其他变量名。 所以当我看到这种面试题,即使能答对,但仍然要因为同名变量耽误那么一下自己再确认下是不是自己眼花会不会看错、甚至猜测你们是不是出题手滑写错了,正常人怎么会写 i:=i 这种不规范的代码,所以又要担心,万一是你们出题错了我答对了会不会反倒被你们判断为答错了。。 同名局部变量这么搞用来迷惑老实人,感觉是跟风 cpp ,多点实在,少整点这种垃圾题目,尤其还有国内 golang 大论坛、公众号,也搞这些带节奏,然后一堆脑残面试官拿去恶心同行,搞得行业面试风气都差得很 隔三岔五看到这类题目就觉得很烦,建议改改 |
2
lifanxi 356 天前
加问一个问题,想保证顺序输出 0,1,2 ,这个程序要怎么改写?
|
3
geelaw 356 天前 1
随便看了一下文档,正确答案不是“乱序输出三个数字”,而是“乱序输出三个数字或者程序在不知道什么时候崩溃”。
https://go.dev/ref/mem > While programmers should write Go programs without data races, there are limitations to what a Go implementation can do in response to a data race. An implementation may always react to a data race by reporting the race and terminating the program. Otherwise, each read of a single-word-sized or sub-word-sized memory location must observe a value actually written to that location (perhaps by a concurrent executing goroutine) and not yet overwritten. 另外 i 和 v 的地址未曾改变不能证明任何事情,即使每次迭代的变量是新的,编译器也可以证明复用旧的内存位置没问题,于是优化之后会看到相同的地址。 |
4
0o0O0o0O0o 356 天前 via iPhone
GOEXPERIMENT=loopvar🥵
|
5
codehz 356 天前 via iPhone
我记得 go 某个版本改了循环的语义啊
你再去问是不是有点不对 |
6
changz 356 天前 via Android
麻烦更新下八股文再发
|
7
adoal 356 天前
谭浩强老师不会老去,只会退休
|
8
Maboroshii 356 天前
正确答案是 不要写这样未知又模棱两可的代码...
|
9
nagisaushio 356 天前 via Android
然而新版本要改了,你版本过时了
|
10
SingeeKing 356 天前
今天发出来是不是有点晚了…… Go1.21 已经通过 GOEXPERIMENT=loopvar 改变了语义,前几天的 1.22rc1 更是作为了默认行为
------ 不过这个面试题可以用来确认他有没有跟随 Go 的最新进度🌚 |
11
SingeeKing 356 天前
另外我倒是觉得这个还是挺重要的,因为和大多的八股不同,Go 因为这个引起的血案不少,很多人不知道(或者知道了写代码时候也不会有意识)循环变量在新起 goroutine 时会复用地址而出问题
---- 但是退一步,知道这个也不代表写的时候能意识到(特别是改之前别人写的代码的时候)…… |
12
GopherDaily OP @lifanxi 那就不能用 goroutine 异步,改成同步变动太大了
|
13
GopherDaily OP |
14
GopherDaily OP @geelaw 对,但如果是考这个点的话,我觉得不适合面试;大多数时候我觉得知道输出的结果就行
|
15
GopherDaily OP @geelaw i/v 的地址不曾改变不是编译器优化的结果,而是明确在 spec 里面的
> Variables declared by the init statement are re-used in each iteration. Link: https://go.dev/ref/spec#For_clause |
16
xiaxiaocao 356 天前
Go 是神奇的语言
const x = 8 var a byte = 1 << x / 2 var y = x var b byte = 1 << y / 2 fmt.Println(a, b) 猜猜输出是什么 |
17
me1onsoda 356 天前
|
18
happyxhw101 356 天前
首先 “正确的答案应该是: 乱序输出三个数字” 这个是不对的,
最有可能的情况是输出 3, 3, 3 |
19
timnottom 356 天前
第一个不应该输出 333 吗???
|
21
MoYi123 356 天前
你这个 c++问 ub 有什么区别?
|
22
RogerBen 356 天前
@xiaxiaocao
我靠,为啥是 0 呢 |
23
ememem0 356 天前 via iPhone 2
自己掌握的知识都是错的,还面试别人。面试者真的惨。
|
24
geelaw 356 天前 1
@GopherDaily #14 错误的方式可以有很多,没有必然理由认为你的错误答案程度更轻,建议不要和别人玩“揣摩出题人意思”的游戏。
#15 你可能没有理解我的意思。 >我们在下述例子中看到, i 和 v 的内存地址始终未曾改变: ... >另外 i 和 v 的地址未曾改变不能证明任何事情,即使每次迭代的变量是新的,编译器也可以证明复用旧的内存位置没问题,于是优化之后会看到相同的地址。 引述第一段内容,阅读后揣摩得出:这段话是想要通过地址不改变 (A) 证明迭代变量每次都是同一个 (B),即论证 A => B ,并观察到 A ,因此推出 B 。 引述第二段内容,意思是 A => B 的论证不恰当,并不是说 A => B 这个命题本身不真。 换言之,A 对论证 B 是无意义的,论证 B 的惟一方式是阅读 Go 的定义。 |
26
RedisMasterNode 356 天前
第一题不是 3 3 3 吗
|
27
monkeyWie 356 天前
这种官方狗屎设计导致的 bug ,有什么好面的,新版本里都要修复了
|
30
rrfeng 356 天前
@xiaxiaocao
var x = 8 var a byte = 1 << x / 2 这样 a 就是 0 ,难道是为了省空间,计算 1<<x 的时候直接在 a 上操作,导致溢出了?? 如果是 const x = 8 的话,const 会在编译的时候计算完 1<<x/2 的结果,于是能够放进 byte 里不会溢出。 |
31
defunct9 356 天前
javascripts 应该是 3 3 3
|
32
faker1 356 天前
这个面试者太惨了,
|
33
ensonmj 356 天前 via iPhone
go 八股文
|