在 for 循环中使用指针 [英] Using Pointers in a for loop

查看:88
本文介绍了在 for 循环中使用指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力理解为什么我的代码在一种状态下有错误,而在另一种状态下没有.我已经有一段时间没有介绍指针了,所以我可能生疏了!

I'm struggling to understand why I have a bug in my code in one state but not the other. It's been a while since I've covered pointers, so I'm probably rusty!

基本上,我有一个存储库结构,用于在内存中存储对象,该结构具有 Store 功能.

Basically I have a repository structure I'm using to store an object in memory, that has a Store function.

type chartsRepository struct {
    mtx    sync.RWMutex
    charts map[ChartName]*Chart
}

func (r *chartsRepository) Store(c *Chart) error {
    r.mtx.Lock()
    defer r.mtx.Unlock()
    r.charts[c.Name] = c
    return nil
}

所以它所做的只是放置一个 RW 互斥锁并将指针添加到映射,由标识符引用.

So all it does is put a RW mutex lock on and adds the pointer to a map, referenced by an identifier.

然后我有一个函数,它基本上会遍历这些对象的一部分,将它们全部存储在存储库中.

Then I've got a function that will basically loop through a slice of these objects, storing them all in the repository.

type service struct {
    charts Repository
}

func (svc *service) StoreCharts(arr []Chart) error {
    hasError := false
    for _, chart := range arr {
        err := svc.repo.Store(&chart)
        // ... error handling
    }
    if hasError {
        // ... Deals with the error object
        return me
    }
    return nil
}

以上不起作用,一开始看起来一切正常,但是在稍后尝试访问数据时,地图中的条目都指向同一个 Chart 对象,尽管有不同的键.

The above doesn't work, it looks like everything works fine at first, but on trying to access the data later, the entries in the map all point to the same Chart object, despite having different keys.

如果我执行以下操作并将指针引用移动到另一个函数,一切都会按预期进行:

If I do the following and move the pointer reference to another function, everything works as expected:

func (svc *service) StoreCharts(arr []Chart) error {
    // ...
    for _, chart := range arr {
        err := svc.storeChart(chart)
    }
    // ...
}

func (svc *service) storeChart(c Chart) error {
    return svc.charts.Store(&c)
}

我假设问题是因为循环覆盖了 for 循环中对 chart 的引用,所以指针引用也会改变.当指针在独立函数中生成时,该引用永远不会被覆盖.是吗?

I'm assuming the issue is that because the loop overwrites the reference to the chart in the for loop, the pointer reference also changes. When the pointer is generated in an independent function, that reference is never overwritten. Is that right?

我觉得我很愚蠢,但是指针不应该由 &chart 生成并且独立于 chart 引用吗?我还尝试在 for 循环中为指针 p := &chart 创建一个新变量,但也没有用.

I feel like I'm being stupid, but shouldn't the pointer be generated by &chart and that's independent of the chart reference? I also tried creating a new variable for the pointer p := &chart in the for loop and that didn't work either.

我应该避免在循环中生成指针吗?

Should I just avoid generating pointers in loops?

推荐答案

这是因为只有一个循环变量 chart,并且在每次迭代中只为其分配一个新值.所以如果你试图取循环变量的地址,它在每次迭代中都是相同的,所以你将存储相同的指针,并且指向的对象(循环变量)在每次迭代中被覆盖(并且在循环之后它将保存在最后一次迭代中分配的值).

This is because there is only a single loop variable chart, and in each iteration just a new value is assigned to it. So if you attempt to take the address of the loop variable, it will be the same in each iteration, so you will store the same pointer, and the pointed object (the loop variable) is overwritten in each iteration (and after the loop it will hold the value assigned in the last iteration).

这在 Spec: For statements: For statements with range 子句中提到:

This is mentioned in Spec: For statements: For statements with range clause:

迭代变量可以通过range"子句使用短变量声明 (:=).在这种情况下,它们的类型设置为各自迭代值的类型,并且它们的 scope 是块for"语句的;它们会在每次迭代中重复使用.如果迭代变量是在for"语句之外声明的,执行后它们的值将是最后一次迭代的值.

The iteration variables may be declared by the "range" clause using a form of short variable declaration (:=). In this case their types are set to the types of the respective iteration values and their scope is the block of the "for" statement; they are re-used in each iteration. If the iteration variables are declared outside the "for" statement, after execution their values will be those of the last iteration.

您的第二个版本有效,因为您将循环变量传递给了一个函数,因此将生成一个副本,然后您存储副本的地址(与循环变量分离).

Your second version works, because you pass the loop variable to a function, so a copy will be made of it, and then you store the address of the copy (which is detached from the loop variable).

你可以在没有函数的情况下达到同样的效果:只需创建一个本地副本并使用它的地址:

You can achieve the same effect without a function though: just create a local copy and use the address of that:

for _, chart := range arr {
    chart2 := chart
    err := svc.repo.Store(&chart2) // Address of the local var
    // ... error handling
}

另请注意,您还可以存储切片元素的地址:

Also note that you may also store the address of the slice elements:

for i := range arr {
    err := svc.repo.Store(&arr[i]) // Address of the slice element
    // ... error handling
}

这样做的缺点是,由于您存储了指向切片元素的指针,因此只要您保留任何指针,切片的整个后备数组就必须保留在内存中(该数组不能被垃圾收集).此外,您存储的指针将与切片共享相同的 Chart 值,因此如果有人修改传递的切片的图表值,这将影响您存储其指针的图表.

The disadvantage of this is that since you store pointers to the slice elements, the whole backing array of the slice would have to be kept in memory for as long as you keep any of the pointers (the array cannot be garbage collected). Moreover, the pointers you store would share the same Chart values as the slice, so if someone would modify a chart value of the passed slice, that would effect the charts whose pointers you stored.

查看相关问题:

Golang:注册多个路由使用循环切片/地图的范围

为什么要这样做两个 for 循环变体给了我不同的行为?

这篇关于在 for 循环中使用指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆