【问题标题】:Implicit memory aliasing in for loopfor循环中的隐式内存别名
【发布时间】:2022-04-25 14:36:36
【问题描述】:

我正在使用 golangci-lint,但在以下代码中出现错误:

versions []ObjectDescription
... (populate versions) ...

for i, v := range versions {
    res := createWorkerFor(&v)
    ...

}

错误是:

G601: Implicit memory aliasing in for loop. (gosec)
                     res := createWorkerFor(&v)
                                            ^

“for 循环中的隐式内存别名”究竟是什么意思?我在 golangci-lint 文档中找不到任何错误描述。我不明白这个错误。

【问题讨论】:

    标签: go static-analysis


    【解决方案1】:

    索引将解决问题:

    for i := range versions {
        res := createWorkerFor(&versions[i])
        ...
    
    }
    

    【讨论】:

    • 但是当我只是将它传递给一个不会泄漏指针的函数时,为什么我需要使用索引呢?
    【解决方案2】:

    简而言之,警告意味着您正在获取循环变量的地址。

    这是因为在for statements 中重复使用了迭代变量。在每次迭代中,将范围表达式中下一个元素的值赋给迭代变量; v 不会改变,只会改变它的值。因此,表达式&v 指的是内存中的同一位置。

    以下代码将相同的内存地址打印四次:

    for _, n := range []int{1, 2, 3, 4} {
        fmt.Printf("%p\n", &n)
    }
    

    当您存储迭代变量的地址时,或者当您在循环内的闭包中使用它时,当您取消引用指针时,它的值可能已经改变。静态分析工具会检测到这一点并发出您看到的警告。

    防止该问题的常见方法是:

    • 索引范围切片/数组/映射。这将获取第 i 个位置的实际元素的地址,而不是迭代变量
    for i := range versions {
        res := createWorkerFor(&versions[i])
    }
    
    • 在循环内重新分配迭代变量
    for _, v := range versions {
        v := v
        res := createWorkerFor(&v) // this is now the address of the inner v
    }
    
    • 使用闭包,将迭代变量作为参数传递给闭包
    for _, v := range versions { 
        go func(arg ObjectDescription) {
            x := &arg // safe
        }(v)
    }
    

    如果您在循环中按顺序取消引用,并且您确定没有任何内容泄漏指针,您可能会忽略此检查而侥幸逃脱。然而,linter 的工作正是报告可能导致问题的代码模式,因此无论如何修复它是个好主意。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-13
      • 2021-10-28
      • 2015-02-25
      • 2018-01-30
      • 2013-03-29
      • 1970-01-01
      • 1970-01-01
      • 2015-08-10
      相关资源
      最近更新 更多