In this post, we will demonstrate the relationship between a slice and its underlying array. To explain this concept, we'll use a code example and walk through it step-by-step.
func main() {
var a [3]int = [3]int{1, 2, 3}
b := a[0:1]
c := a[0:2]
b = append(b, 4) // grows b, mutates a
fmt.Printf("a[%p]= %v\n", &a, a) // a[0xc0000121f8]= [1, 4, 3]
fmt.Printf("b[%p]= %[1]v\n", b) // b[0xc0000121f8]= [1, 4]
c = append(c, 5) // grows c, mutates a
fmt.Printf("a[%p]= %v\n", &a, a) // a[0xc0000121f8]= [1, 4, 5]
fmt.Printf("c[%p]= %[1]v\n", c) // c[0xc0000121f8]= [1, 4, 5]
c = append(c, 6) // because cap c is 3, this operation forces to reallocate c with doubling cap
fmt.Printf("a[%p]= %v\n", &a, a) // a[0xc0000121f8]= [1, 4, 5]
fmt.Printf("c[%p]= %[1]v\n", c) // c[0xc0000145d0]= [1, 4, 5, 6]
fmt.Println("cap c: ", cap(c)) // cap c: 6
c[0] = 9 // mutates a different array!
fmt.Printf("a[%p]= %v\n", &a, a) // a[0xc0000121f8]= [1, 4, 5]
fmt.Printf("c[%p]= %[1]v\n", c) // c[0xc0000145d0]= [9, 4, 5, 6]
}
1-initialization:
So when we define a, b and c we some something like this image.
As we can see a is an array and b, c is slices that a is their underlying array.
2- b = append(b, 4)
• b has a length of 1 and a capacity of 3. There is room to add elements.
• No reallocation is needed. The append operation adds the value 4 into the next available spot in the underlying array a.
• This overwrites a[1].
• a is mutated.
3- c = append(c, 5)
• c has a length of 2 and a capacity of 3. There is still room.
• No reallocation is needed. The append adds 5 into the next spot in the underlying array a.
• This overwrites a[2].
• a is mutated again.
4- c = append(c, 6)
• c now has a length of 3 and a capacity of 3. There is no more room.
• To add 6, append must perform a reallocation.
• It creates a new, larger array (with a new capacity, typically doubled to 6), copies the elements [1, 4, 5] over, and then appends 6.
• The slice c is updated to point to this new array. It no longer shares data with a.
• a is not affected.
5- c[0] = 9
• This modifies the first element of the slice c.
• Since c now points to its own separate, underlying array, this change only affects c.
• a remains completely untouched.
code output
a[0xc0000121f8]= [1 4 3]
b[0xc0000121f8]= [1 4]
a[0xc0000121f8]= [1 4 5]
c[0xc0000121f8]= [1 4 5]
a[0xc0000121f8]= [1 4 5]
c[0xc0000145d0]= [1 4 5 6]
cap c: 6
a[0xc0000121f8]= [1 4 5]
c[0xc0000145d0]= [9 4 5 6]
The memory addresses shown in the output (like 0xc0000121f8) are specific to a single execution of the program. When you run the code on your machine, the operating system will allocate memory at different addresses.
Top comments (0)