for 循环中的隐式内存别名

Dea*_*ean 15 static-analysis go

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

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

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

}
Run Code Online (Sandbox Code Playgroud)

错误是:

G601: Implicit memory aliasing in for loop. (gosec)
                     res := createWorkerFor(&v)
                                            ^
Run Code Online (Sandbox Code Playgroud)

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

小智 30

索引将解决问题:

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

}
Run Code Online (Sandbox Code Playgroud)


bla*_*een 7

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

发生这种情况是因为在for语句中重复使用了迭代变量。在每次迭代中,范围表达式中下一个元素的值被赋给迭代变量;v不会变,只是它的值变了。因此,该表达式&v指的是内存中的相同位置。

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

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

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

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

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