golang中for range的取地址操作陷阱介绍

Tips:for range创建了每个元素的副本,而不是直接返回每个元素的引用

例子1:

package main
import "fmt"
func main() {
 slice := []int{0, 1, 2, 3}
 myMap := make(map[int]*int)
 for index, value := range slice {
  myMap[index] = &value
 }
 fmt.Println("=====new map=====")
 prtMap(myMap)
}

func prtMap(myMap map[int]*int) {
 for key, value := range myMap {
  fmt.Printf("map[%v]=%v\n", key, *value)
 }
}

输出:

dotzdeMacBook-Pro-2:src dotz$ ./range

=====new map=====

map[0]=3

map[1]=3

map[2]=3

map[3]=3

例子2:

package main
import "fmt"
type Test struct {
    name string
}

func (this *Test) Point() { // this  为指针
    fmt.Println(this.name)
}

func main() {
    ts := []Test{{"a"}, {"b"}, {"c"}}
    for _, t := range ts {
        defer t.Point() //输出 c c c
    }
} 

输出:

dotzdeMacBook-Pro-2:src dotz$ ./method

c

c

c

例子1 我们预期输出0,1,2,3,例子2 我们预期输出a,b, c,但两个例子的输出都不是我们预期的。

对于例子1,比较明显,执行了取地址操作,每次都取value变量的地址,所以最后map中的所有元素的值都是value变量的地址(引用),因为最后value被赋值为3,所有输出都是3.

对于例子2,隐晦一点,夹杂了defer和方法接收者的规则,但其实也和例子1一样,执行t.Point()时,得到的是t的地址(引用),for结束时,t被赋值为”c“的地址,main函数返回时,都在执行”c“的接收方法Point,所以输出都是”c".

补充:golang取地址操作采坑:for idx,item := range arr中的item是个独立对象

先看代码:

package main
import "fmt"
func main() {
    type s struct {
        A string
        B int32
    }
    arr := []s{
        {"123", 123},
        {"456", 456},
        {"789", 789},
    }
    m := make(map[string]*s)
    for idx, item := range arr {
        m[item.A] = &item
        fmt.Printf("idx=%d, addr=%p, item addr=%p\n", idx, &arr[idx], &item)
    }
    for k, v := range m {
        fmt.Printf("key=%s, v=%+v\n", k, v)
    }
}

运行输出:

idx=0, addr=0xc00004e050, item addr=0xc0000044a0

idx=1, addr=0xc00004e068, item addr=0xc0000044a0

idx=2, addr=0xc00004e080, item addr=0xc0000044a0

key=123, v=&{A:789 B:789}

key=456, v=&{A:789 B:789}

key=789, v=&{A:789 B:789}

我傻傻的在循环中取item的地址,结果所有map中的值都指向最后一个!

看来item是一个独立对象,这个对象指向了数组中的对应元素。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • 关于Golang中range指针数据的坑详解

    前言 在Golang中使用 for range 语句进行迭代非常的便捷,但在涉及到指针时就得小心一点了. 下面的代码中定义了一个元素类型为 *int 的通道 ch : package main import ( "fmt" ) func main() { ch := make(chan *int, 5) //sender input := []int{1,2,3,4,5} go func(){ for _, v := range input { ch <- &v } cl

  • 解决Golang map range遍历结果不稳定问题

    闲言少叙,本文主要是想介绍一个Golang开发常见的一个问题.然而,此问题对于初学者来说却经常容易陷入坑中. 问题 我在写一段代码时,使用了Golang的map数据结构,目的是想用map缓存计数结果.简单来说map的键也是整型的,且以递增顺序存储.我的最初想法是,在统计结束后,按照map中存储的键有序输出值.可是,当我运行程序时,结果并不是我想要的,而且有一定概率运行结果不同. 问题代码 func sortByBits(arr []int) []int { var bitmap = make(m

  • golang中range在slice和map遍历中的注意事项

    golang中range在slice和map遍历中的注意事项 package main import ( "fmt" ) func main() { slice := []int{0, 1, 2, 3} myMap := make(map[int]*int) for _,v :=range slice{ if v==1 { v=100 } } for k,v :=range slice{ fmt.Println("k:",k,"v:",v) }

  • golang中for range的取地址操作陷阱介绍

    Tips:for range创建了每个元素的副本,而不是直接返回每个元素的引用 例子1: package main import "fmt" func main() { slice := []int{0, 1, 2, 3} myMap := make(map[int]*int) for index, value := range slice { myMap[index] = &value } fmt.Println("=====new map=====")

  • python 中dict的元素取值操作

    如下所示: dict.get(key, default=None) key – 字典中要查找的键. default – 如果指定键的值不存在时,返回该默认值值. {'1*': 9, '2*': 6, '**': 15}.values() Out[377]: dict_values([9, 6, 15]) {'1*': 9, '2*': 6, '**': 15}.keys() Out[378]: dict_keys(['1*', '2*', '**']) {'1*': 9, '2*': 6, '*

  • Golang中的sync包的WaitGroup操作

    sync的waitgroup功能 WaitGroup 使用多线程时,进行等待多线程执行完毕后,才可以结束函数,有两个选择 channel waitgroup 首先使用channel func add (n *int , isok chan bool){ for i :=0 ;i <1000 ; i ++ { *n = *n + 1 } isok <- true } func main () { var ok = make(chan bool , 2) var i,u = 0,0 go add(

  • c++函数中的指针参数与地址参数区别介绍

    比如 一个函数 chat(link &a): chat(ling *a): 前者引入一个地址做形参 是不是可以把一个指针变量p.. 这么用chat(p): 那跟第二个函数 有什么区别呢 都是传地址啊.. 小弟弄不明白~~ chat(int&a); chat(int *a); 这两个函数是完全不同意义的东西,你的理解主要是在int&a和int* a这个类型上面.要注意int&和int*是两个完全不同的类型.int&是引用类型,而int*是指向int类型变量的指针类型.

  • 一文带你了解Golang中select的实现原理

    目录 概述 结构 现象 非阻塞的收发 随机执行 编译 直接阻塞 独立情况 非阻塞操作 通用情况 运行时 初始化 循环 总结 概述 select是go提供的一种跟并发相关的语法,非常有用.本文将介绍 Go 语言中的 select 的实现原理,包括 select 的结构和常见问题.编译期间的多种优化以及运行时的执行过程. select 是一种与 switch 非常相似的控制结构,与 switch 不同的是,select 中虽然也有多个 case,但是这些 case 中的表达式都必须与 Channel

  • 解决golang中container/list包中的坑

    golang中list包用法可以参看这篇文章 但是list包中大部分对于e *Element进行操作的元素都可能会导致程序崩溃,其根本原因是e是一个Element类型的指针,当然其也可能为nil,但是golang中list包中函数没有对其进行是否为nil的检查,变默认其非nil进行操作,所以这种情况下,便可能出现程序崩溃. 1.举个简单例子 Remove()函数 package main import ( "container/list" "fmt" ) func

  • Golang中channel的原理解读(推荐)

    数据结构 channel的数据结构在$GOROOT/src/runtime/chan.go文件下: type hchan struct { qcount uint // 当前队列中剩余元素个数 dataqsiz uint // 环形队列长度,即可以存放的元素个数 buf unsafe.Pointer // 环形队列指针 elemsize uint16 // 每个元素的大小 closed uint32 // 标记是否关闭 elemtype *_type // 元素类型 sendx uint //

  • 解析Golang中的GoPath和GoModule

    目录 什么是GoPath? 什么是GoModule? GoModule的设置 GoModule无法下载国外的依赖包问题在Golang中,有两个概念非常容易弄错,第一个就是GoPath,第二个则是GoModule,很多初学者不清楚这两者之间的关系,也就难以清晰地了解项目的整体结构,自然也就难以编写结构清晰的代码. 什么是GoPath? 什么是Gopath?在我的上一篇博客Golang环境安装&IDEA开发Golang中,曾经提到过GoPath的概念.GoPath是Golang的工作空间,所有的Go

  • golang中的defer函数理解

    目录 golang的defer 什么是defer 理解defer defer什么时间执行(defer. return.返回值 三者的执行顺序) defer输出的值,就是定义时的值.而不是defer真正执行时的变量值(注意引用情况) 多个defer,执行顺序 defer的函数一定会执行么? panic情况 os.Exit情况 kill情况(Ctrl+C) 参考文献 golang的defer 什么是defer defer的的官方文档:https://golang.org/ref/spec#Defer

  • golang中包无法引入问题解决

    目录 前言 问题背景 问题现象 问题解决 1.强制开启GO111MODULE 2.切换代理 问题总结 前言 刚接触golang不久,有些环境无法融会贯通,现在针对开发过程中遇到的问题做个排查记录 问题背景 开发环境区分不同网段,同一个程序引入到另一个环境后,对应的包无法下载 问题现象 程序导入另一个环境后,初始化操作就是下载包:执行go mod tidy后,出现下面错误 go: github.com/Sirupsen/logrus@v1.0.1: Get " https://proxy.gola

随机推荐