Readme

for range的一个坑 #

for range是值拷贝出来的副本

在使用for range的时候,要注意的是,不管是slice还是map,循环的值都是被range值拷贝出来的副本值。 举个简单的例了

对于list

    var t []Test
	t = append(t, Test{Index: 1, Num: 1})
	t = append(t, Test{Index: 2, Num: 2})

	// 实际上没有成功修改t.Num,因为是副本复制
	for _, v := range t {
		v.Num += 100
	}

	for _, v := range t {
		// 输出
		// 1 1
		// 2 2
		fmt.Println(v.Index, v.Num)
	}

对于 map, 也不能这么搞,实际上都是复制


	m := make(map[int]Test)
	m[0] = Test{Index: 1, Num: 1}
	m[1] = Test{Index: 2, Num: 2}
	for _, v := range m {
		v.Num += 100
	}
	for _, v := range m {
		// 输出(可以乱序)
		// 1 1
		// 2 2
		fmt.Println(v.Index, v.Num)
	}

怎么做? #

两个办法,用下标(map也一样)

	for i := range t {
		t[i].Num += 100
		fmt.Println(t[i].Num)
		// 输出(可以乱序)
		// 101 102
	}

用指针

	var t2 []*Test
	t2 = append(t2, &Test{Index: 1, Num: 1})
	t2 = append(t2, &Test{Index: 2, Num: 2})

	for k, v := range t2 {
		v.Num += 100
		fmt.Println(t2[k].Num)
		// 输出(可以乱序)
		// 101 102
	}

for range 原理 #

通过查看 源代码 ,我们可以发现for range的实现是:

# statements.cc:6419 (441f3f1 on 4 Oct)
// Arrange to do a loop appropriate for the type.  We will produce
//   for INIT ; COND ; POST {
//           ITER_INIT
//           INDEX = INDEX_TEMP
//           VALUE = VALUE_TEMP // If there is a value
//           original statements
//   }

并且对于Slice,Map等各有具体不同的编译实现,我们先看看for range slice的具体实现

# statements.cc:6638  (441f3f1 on 4 Oct)
// The loop we generate:
//   for_temp := range
//   len_temp := len(for_temp)
//   for index_temp = 0; index_temp < len_temp; index_temp++ {
//           value_temp = for_temp[index_temp]
//           index = index_temp
//           value = value_temp
//           original body
//   }

先是对要遍历的 Slice 做一个拷贝,获取长度大小,然后使用常规for循环进行遍历,并且返回值的拷贝。 再看看for range map的具体实现:

# statements.cc:6891  (441f3f1 on 4 Oct)
// The loop we generate:
//   var hiter map_iteration_struct
//   for mapiterinit(type, range, &hiter); hiter.key != nil; mapiternext(&hiter) {
//           index_temp = *hiter.key
//           value_temp = *hiter.val
//           index = index_temp
//           value = value_temp
//           original body
//   }

也是先对map进行了初始化,因为map是hashmap,所以这里其实是一个hashmap指针的拷贝。

引用: Go 中for range的一个坑



本图书由小熊©2021 版权所有,所有文章采用知识署名-非商业性使用-禁止演绎 4.0 国际进行许可。