ale*_*leb 3 arrays iteration pointers go
迭代具有范围的数组时,如果更新了数组,则更新后的位置不会进入未来的循环运行。以下打印“1 2”而不是“1 0”
package main
import (
"fmt"
)
func main() {
var A = &[2]int{1, 2}
for i, v := range A {
if i == 0 {
A[1] = 0
}
fmt.Print(v, " ")
}
fmt.Println()
var B = [2]int{1, 2}
for i, v := range B {
if i == 0 {
B[1] = 0
}
fmt.Print(v, " ")
}
}
Run Code Online (Sandbox Code Playgroud)
https://play.golang.org/p/0zZY6vjxwut
看起来数组在迭代之前就被复制了。
规范的哪一部分描述了这种行为?请参阅https://golang.org/ref/spec#For_range 上的“带有范围子句的语句”
TLDR;无论你的范围是什么,都会有一个副本(这是一般的“规则”,但有一个例外,见下文)。数组在 Go 中很少见,通常使用切片。切片值(切片标头)包含一个指向底层数组的指针,因此复制切片标头是快速、高效的,并且它不复制切片元素,不像数组。在这方面,遍历指向数组的指针类似于遍历切片。
范围表达式
x在开始循环之前计算一次,但有一个例外:如果最多存在一个迭代变量并且len(x)是常量,则不计算范围表达式。
数组是值,它们不包含指向位于数组内存之外的数据的指针(与切片不同)。Go 博客:Go Slices:用法和内部结构:
Go 的数组是值。数组变量表示整个数组;它不是指向第一个数组元素的指针(就像在 C 中的情况一样)。这意味着当您分配或传递数组值时,您将复制其内容。(为了避免复制,你可以传递一个指向数组的指针,但那是指向数组的指针,而不是数组。)考虑数组的一种方法是一种结构,但带有索引而不是命名字段:一个固定的-size 复合值。
评估一个数组是整个数组的副本,它是所有元素的副本。规格:变量:
在你的第一个例子中,范围表达式只是一个指向数组的指针,所以只复制了这个指针(而不是指向的数组),所以当你这样做时A[1] = 0(这是 的简写(*A)[1] = 0),你修改原始数组,然后迭代变量从指向的数组中获取元素。
在第二个实施例的范围内表达是阵列,以使阵列(及其所有元件)被复制,并且在其内部B[1] = 0仍修改原始阵列(B是一个变量,而不是范围表达式的求值的结果),但v是副本的一个元素(v在每次迭代中从复制的数组中填充)。
那么这个“复制”是如何实现的呢?编译器生成用于将for range范围表达式的结果复制(分配)到临时变量的代码(如果需要,因为可能并不总是需要它:“如果最多存在一个迭代变量并且len(x)是常量,则范围表达式是未评估”)。
可以在cmd/compile/internal/gc/range.go文件中检查此代码。
请参阅相关文章:Go Range Loop Internals