我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Go for range中容易踩的坑有哪些

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Go for range中容易踩的坑有哪些

这篇文章主要介绍了Go for range中容易踩的坑有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Go for range中容易踩的坑有哪些文章都会有所收获,下面我们一起来看看吧。

1. for+传值

先来到开胃菜,热热身~

type student struct {  name string  age  int}func main() {  m := make(map[string]student)  stus := []student{    {name: "张三", age: 18},    {name: "李四", age: 23},    {name: "王五", age: 26},  }  for _, stu := range stus {    m[stu.name] = stu  }  for k, v := range m {    fmt.Println(k, "=>", v.name)  }}

不出意料,输出结果为

李四 => 李四
王五 => 王五
张三 => 张三

这题比较简单,就是简单的传值操作,大家应该都能答上来。下面加大难度,改为传址操作

2. for+传址

将案例一改为传址操作

type student struct {  name string  age  int}func main() {  m := make(map[string]*student)  stus := []student{    {name: "张三", age: 18},    {name: "李四", age: 23},    {name: "王五", age: 26},  }  for _, stu := range stus {    m[stu.name] = &stu  }  for k, v := range m {    fmt.Println(k, "=>", v.name)  }}

好好想想应该输出什么结果呢?还是跟案例一是一样的结果吗?难道会有坑?

不出意料,还是出了意外,输出结果为

张三 => 王五
李四 => 王五
王五 => 王五

为什么呢?

  • 首先,关键点在于Go的for循环,对循环变量stu每次是循环并不是迭代(简单的说,就是对循环变量stu只会做一次声明和内存地址的分配,后面循环就是不断更新值);

  • 所以,取址操作 &stu,其实都是取的同一个变量的地址,只是值被循环更新为最后一个元素的值;

  • 最终,输出的v.name,都是最后一个元素的name为王五

解决方案

在for循环中,做同名变量覆盖stu:=stu(即重新声明一个局部变量,做值拷贝,避免相互影响)

type student struct {  name string  age  int}func main() {  m := make(map[string]*student)  stus := []student{    {name: "张三", age: 18},    {name: "李四", age: 23},    {name: "王五", age: 26},  }  for _, stu := range stus {    stu := stu  //同名变量覆盖    m[stu.name] = &stu  }  for k, v := range m {    fmt.Println(k, "=>", v.name)  }}

输出结果:

张三 => 张三
李四 => 李四
王五 => 王五

3.for+闭包

在for循环里,做闭包操作,也是很容易掉坑的。看看下面输出什么?

var prints []func()for _, v := range []int{1, 2, 3} {  prints = append(prints, func() { fmt.Println(v) })}for _, print := range prints {  print()}

一眼看过去,感觉是输出1 2 3,但实际会输出 3 3 3

为什么呢?

  • 首先,在分析了案例二后,我们知道了Go的for循环对循环变量v,其实每次是循环并不是迭代;

  • 然后,闭包=函数+引用环境,在同一个引用环境下,循环变量v的值会被不断的覆盖;

  • 所以最终,在打印时,输出的v,都是最后一个值3。

解决方案

和案例二解决方案一样,是在for循环中,做同名变量覆盖v:=v

var prints []func()for _, v := range []int{1, 2, 3} {  v := v //同名变量覆盖    prints = append(prints, func() { fmt.Println(v) })}for _, print := range prints {  print()}

输出结果:

1
2
3

4. for+goroutine

在for循环里,起goroutine协程,也是很迷惑很容易掉坑的。看看下面输出什么?

var wg sync.WaitGroupstrs := []string{"1", "2", "3", "4", "5"}for _, str := range strs {  wg.Add(1)  go func() {    defer wg.Done()    fmt.Println(str)  }()}wg.Wait()

一眼看过去,感觉是会无序输出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

var wg sync.WaitGroupstrs := []string{"1", "2", "3", "4", "5"}for _, str := range strs {  str := str //同名变量覆盖  wg.Add(1)  go func() {    defer wg.Done()    fmt.Println(str)  }()}wg.Wait()

输出结果:

5
4
2
1
3

注意是1~5无序输出

关于“Go for range中容易踩的坑有哪些”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Go for range中容易踩的坑有哪些”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Go for range中容易踩的坑有哪些

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Go for range中容易踩的坑有哪些

这篇文章主要介绍了Go for range中容易踩的坑有哪些的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Go for range中容易踩的坑有哪些文章都会有所收获,下面我们一起来看看吧。1. for+传值先来
2023-07-05

简单聊聊Go for range中容易踩的坑

for循环问题,在面试中经常都会被问到,并且在实际业务项目中也经常用到for循环,要是没用好,一不下心就掉坑,本文就来讲讲Go for range中容易踩的坑吧
2023-03-20

golang for range中的坑有哪些

本篇内容主要讲解“golang for range中的坑有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“golang for range中的坑有哪些”吧!go 语言 for range 中的
2023-07-04

Java入门易踩坑的问答题有哪些

本篇内容介绍了“Java入门易踩坑的问答题有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1,java基本类型下面属于java基本数据类
2023-06-29

Go Slice扩容的坑有哪些及怎么解决

这篇文章主要介绍“Go Slice扩容的坑有哪些及怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go Slice扩容的坑有哪些及怎么解决”文章能帮助大家解决问题。知识重温切片底层结构定义:包
2023-07-05

SEO中容易被忽视的细节分析有哪些

这篇文章主要为大家展示了“SEO中容易被忽视的细节分析有哪些”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“SEO中容易被忽视的细节分析有哪些”这篇文章吧。第一,标签的应用,建设比较早的站容易出现
2023-06-13

C#编程中最容易犯的编写错误有哪些

这篇文章主要讲解了“C#编程中最容易犯的编写错误有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#编程中最容易犯的编写错误有哪些”吧!1、拼接字符串在C#编程中,字符串类型的处理是比较
2023-07-05

网站建设中容易忽略的重要细节有哪些

小编给大家分享一下网站建设中容易忽略的重要细节有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、网站链接不连贯有一些网站的技术人员,在进行网站建设的时候,或
2023-06-10

企业网站在建设环节中容易忽视的问题有哪些

本篇内容介绍了“企业网站在建设环节中容易忽视的问题有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!  第一,定位问题。很多企业网站对于网
2023-06-10

Java编程中最容易忽略的10个常见问题分别有哪些

本篇文章给大家分享的是有关Java编程中最容易忽略的10个常见问题分别有哪些,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。在Java编码中,我们容易犯一些错误,也容易疏忽一些问
2023-06-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录