Golang-Append

这几天在准备面试, 想起之前被问到的一个全排序的问题, 今天用golang写了一遍, 无意中发现了一个冷门的知识(或者叫).

看下面一段代码:

var NUMS = []int{1, 2, 3}

func main() {
	x1 := NUMS[:1]
	x2 := NUMS[2:]
	fmt.Println(x1, x2, NUMS)
	fmt.Println(append(x1, x2...))
	fmt.Println(x1, x2, NUMS)
}

大脑编译一下, 结果会是什么?

[1] [3] [1 2 3]
[1 3]
[1] [3] [1 2 3]

是上面这样吗?

首先截取了 NUMS 的第一个作为 x1(1), 然后截取 NUMS 的第二个之后的, 作为 x2(3), 然后输出 x1, x2NUMS; 然后将 x1 x2 连接起来输出(1 3); 然后重复第一次的输出.

好像没问题吧?

但是.

https://play.golang.org/p/CugWJ9tP34Q

结果却有点出人意料:

[1] [3] [1 2 3]
[1 3]
[1] [3] [1 3 3]

为什么 NUMS 会被修改呢???

没人碰它啊! 除了… append ???

然后, 就发现了这样一个坑, append 的确会修改数组.

See: https://stackoverflow.com/questions/35920534/why-does-append-modify-passed-slice

Appending to and copying slices

The built-in functions append and copy assist in common slice operations. For both functions, the result is independent of whether the memory referenced by the arguments overlaps.

The variadic function append appends zero or more values x to s of type S, which must be a slice type, and returns the resulting slice, also of type S. The values x are passed to a parameter of type …T where T is the element type of S and the respective parameter passing rules apply. As a special case, append also accepts a first argument assignable to type []byte with a second argument of string type followed by …. This form appends the bytes of the string.

append(s S, x ...T) S // T is the element type of S

If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.

注意上面加粗的引用部分, 如果 append(slice, elements...) 中的slice有足够的空间(cap)容纳 elements的话, 那就直接复用 slice 了. 也就是说, 将slice中的某些值, 修改为追加的值.

所以, 当我们 append(x1, x2...) 的时候(注意, x1在底层还是引用的NUMS), 因为NUMS有足够的空间容纳 x2, 所以append直接将NUMS[1] 修改为了 x2[0], NUMS也就变成了 [1, 3, 3].

所以, 如果想要不修改NUMS的值, 则需要先copy一份, 比如:

var NUMS = []int{1, 2, 3}

func main() {
	tempNums := make([]int, len(NUMS))
	copy(tempNums, NUMS)
	x1 := tempNums[:1]
	x2 := tempNums[2:]

	fmt.Println(x1, x2, NUMS)
	fmt.Println(append(x1, x2...))
	fmt.Println(x1, x2, NUMS)
}

这样所有的修改就都不会影响到初始的NUMS了.


附上数组全排序: https://play.golang.org/p/sKE2jouqKB_4

comments powered by Disqus