for循环问题,在面试中经常都会被问到,并且在实际业务项目中也经常用到for循环,要是没用好,一不下心就掉坑。
下面会挑选几个经典的案例,一块来探讨下,看看如何避免掉坑,多积累积累采坑经验。
案例一:for+传值
先来到开胃菜,热热身~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
不出意料,输出结果为:
李四 => 李四
王五 => 王五
张三 => 张三
这题比较简单,就是简单的传值操作,大家应该都能答上来。下面加大难度,改为传址操作
案例二:for+传址
将案例一改为传址操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
好好想想应该输出什么结果呢?还是跟案例一是一样的结果吗?难道会有坑?
不出意料,还是出了意外,输出结果为:
张三 => 王五
李四 => 王五
王五 => 王五
为什么呢?
- 首先,关键点在于Go的for循环,对
循环变量stu
每次是循环并不是迭代(简单的说,就是对循环变量stu
只会做一次声明和内存地址的分配,后面循环就是不断更新值); - 所以,取址操作
&stu
,其实都是取的同一个变量的地址,只是值被循环更新为最后一个元素的值; - 最终,输出的
v.name
,都是最后一个元素的name为王五
。
解决方案:
在for循环中,做同名变量覆盖stu:=stu
(即重新声明一个局部变量,做值拷贝,避免相互影响)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
输出结果:
张三 => 张三
李四 => 李四
王五 => 王五
案例三:for+闭包
在for循环里,做闭包操作,也是很容易掉坑的。看看下面输出什么?
1 2 3 4 5 6 7 |
|
一眼看过去,感觉是输出1 2 3,但实际会输出 3 3 3
为什么呢?
- 首先,在分析了案例二后,我们知道了Go的for循环对循环变量v,其实每次是循环并不是迭代;
- 然后,闭包=函数+引用环境,在同一个引用环境下,循环变量v的值会被不断的覆盖;
- 所以最终,在打印时,输出的v,都是最后一个值3。
解决方案:
和案例二解决方案一样,是在for循环中,做同名变量覆盖v:=v
1 2 3 4 5 6 7 8 |
|
输出结果:
1
2
3
案例四:for+goroutine
在for循环里,起goroutine协程,也是很迷惑很容易掉坑的。看看下面输出什么?
1 2 3 4 5 6 7 8 9 10 |
|
一眼看过去,感觉是会无序输出1 2 3 4 5,但实际会输出 5 5 5 5 5
为什么呢?
- 首先,要记得Go的for循环对循环变量str,其实每次是循环并不是迭代;
- 然后,main协程会和新起的协程做相互博弈,看谁执行更快,按这个案例执行情况来看,main协程执行速度明显比新起的协程会更快,所以str被更新为最后一个元素值5(备注:并非绝对);
- 最终,在新起的协程中,使用str时值都为5,作为结果去输出;
- 拓展:如果在新起协程前,sleep个5s,输出结果又会截然不同,感兴趣的同学可以自行实验下,然后逐步深入地了解下GMP调度机制。
解决方案:
和前面两个案例解决方案一样,是在for循环中,做同名变量覆盖str:=str
1 2 3 4 5 6 7 8 9 10 11 |
|
输出结果:
5
4
2
1
3
注意是1~5无序输出
总结
for循环中做传址、闭包、goroutine相关操作,千万要注意,一不小心就会很容易掉坑。
到此这篇关于Go语言中for循环的经典案例分析的文章就介绍到这了,希望可以帮到你!