问题
The program below has unexpected output.
func main(){
s:=[]int{5}
s=append(s,7)
s=append(s,9)
x:=append(s,11)
y:=append(s,12)
fmt.Println(s,x,y)
}
output: [5 7 9] [5 7 9 12] [5 7 9 12]
Why is the last element of x
12
?
回答1:
A slice is only a window over part of an array, it has no specific storage.
This means that if you have two slices over the same part of an array, both slices must "contain" the same values.
Here's exactly what happens here :
- When you do the first
append
, you get a new slice of size2
over an underlying array of size2
. - When you do the next
append
, you get a new slice of size3
but the underlying array is of size4
(append
usually allocates more space than the immediately needed one so that it doesn't need to allocate at every append). - This means the next
append
doesn't need a new array. Sox
andy
both will use the same underlying array as the precedent slices
. You write11
and then12
in the same slot of this array, even if you get two different slices (remember, they're just windows).
You can check that by printing the capacity of the slice after each append :
fmt.Println(cap(s))
If you want to have different values in x
and y
, you should do a copy, for example like this :
s := []int{5}
s = append(s, 7)
s = append(s, 9)
x := make([]int,len(s))
copy(x,s)
x = append(x, 11)
y := append(s, 12)
fmt.Println(s, x, y)
Another solution here might have been to force the capacity of the array behind the s
slice to be not greater than the needed one (thus ensuring the two following append
have to use a new array) :
s := []int{5}
s = append(s, 7)
s = append(s, 9)
s = s[0:len(s):len(s)]
x := append(s, 11)
y := append(s, 12)
fmt.Println(s, x, y)
See also Re-slicing slices in Golang
回答2:
dystroy explained it very well. I like to add a visual explanation to the behaviour.
A slice is only a descriptor of an array segment. It consists of a pointer to the array (ptr), the length of the segment (len), and capacity (cap).
+-----+
| ptr |
|*Elem|
+-----+
| len |
|int |
+-----+
| cap |
|int |
+-----+
So, the explanation of the code is as follow;
func main() {
+
|
s := []int{5} | s -> +-----+
| []int | ptr +-----> +---+
| |*int | [1]int| 5 |
| +-----+ +---+
| |len=1|
| |int |
| +-----+
| |cap=1|
| |int |
| +-----+
|
s = append(s,7) | s -> +-----+
| []int | ptr +-----> +---+---+
| |*int | [2]int| 5 | 7 |
| +-----+ +---+---+
| |len=2|
| |int |
| +-----+
| |cap=2|
| |int |
| +-----+
|
s = append(s,9) | s -> +-----+
| []int | ptr +-----> +---+---+---+---+
| |*int | [4]int| 5 | 7 | 9 | |
| +-----+ +---+---+---+---+
| |len=3|
| |int |
| +-----+
| |cap=4|
| |int |
| +-----+
|
x := append(s,11) | +-------------+-----> +---+---+---+---+
| | | [4]int| 5 | 7 | 9 |11 |
| | | +---+---+---+---+
| s -> +--+--+ x -> +--+--+
| []int | ptr | []int | ptr |
| |*int | |*int |
| +-----+ +-----+
| |len=3| |len=4|
| |int | |int |
| +-----+ +-----+
| |cap=4| |cap=4|
| |int | |int |
| +-----+ +-----+
|
y := append(s,12) | +-----> +---+---+---+---+
| | [4]int| 5 | 7 | 9 |12 |
| | +---+---+---+---+
| |
| +-------------+-------------+
| | | |
| s -> +--+--+ x -> +--+--+ y -> +--+--+
| []int | ptr | []int | ptr | []int | ptr |
| |*int | |*int | |*int |
| +-----+ +-----+ +-----+
| |len=3| |len=4| |len=4|
| |int | |int | |int |
| +-----+ +-----+ +-----+
| |cap=4| |cap=4| |cap=4|
| |int | |int | |int |
+ +-----+ +-----+ +-----+
fmt.Println(s,x,y)
}
来源:https://stackoverflow.com/questions/27568213/could-anyone-explain-this-strange-behaviour-of-appending-to-golang-slices