[Golang] The underlying implementation of slicing (about the problem of allocating a new array after slice calls the append function)

Problem Description

I encountered a very strange phenomenon when writing code today, first look at the following two pieces of code

func push(a []int, v int) {
    
    
	a[1] = 2
	a = append(a, v)
}
func main() {
    
    
	a := []int{
    
    0, 1, 2}
	push(a, 3)
	fmt.Println(a)
}

result:[0 2 2]

func push(a []int, v int) {
    
    
	a = append(a, v)
	a[1] = 2
}
func main() {
    
    
	a := []int{
    
    0, 1, 2}
	push(a, 3)
	fmt.Println(a)
}

result:[0 1 2]

At first glance, these two pieces of code are almost identical, the only difference is that the order of the two lines of code in the push function is inconsistent

There are two problems in these two pieces of code

  1. Why does the assignment statement work in the first piece of code, but append doesn't work?
  2. Why neither the assignment statement nor append in the second code works

problem analysis

The first question: Why does the assignment statement work in the first piece of code, but append does not work?

First of all, we need to be clear that there is no reference transfer in Go language , that is, a []int here is value transfer, we might as well output the address of a

insert image description here
It can be seen that a inside and outside the function is not the same slice, so since it is not the same slice, why in the first piece of code, if a inside the function is modified, a outside the function will also change?


Here we need to understand how slices are implemented in go language

As you can see in the figure below, the slice in go language is actually a view
slice of the underlying array, which consists of three parts, namely the pointer ptr pointing to the underlying array , the length of the slice len , and the length cap
insert image description here
of the underlying array. This can explain why Modify the slice inside the function in the first piece of code, and the slice outside the function will also change. Although the addresses of the two slices are different, their two values ​​are the same, that is to say, their two internal ptrs are the same Both point to the same underlying array, so modifying one will change the other. Similarly, when appending inside a function, the slice len inside the function increases, but because the value is passed, the slice len outside the function does not change, so the slice append inside the function will not cause the slice outside the function to change.

The second question: why neither the assignment statement nor append in the second code works

First of all, why append does not work has been explained above, and here we focus on why the assignment statement does not work

The reason is only one sentence: if the slice exceeds the cap when adding elements, then it is no longer a view of the original array, and the system will reallocate a larger underlying array

Continue to analyze the previous code, and then output the len and cap of the slice based on the output address
insert image description here

It can be seen that before executing append, the len of the slice is equal to cap. After executing append, the length of the slice will exceed cap. At this time, the system will reallocate a larger array. Observing the output, it can be found that the cap of the slice has changed after append is executed, which is consistent with our assumption. The system reallocates a larger array to the slice, and the ptr pointer of the slice points to another array, which is different from the slice outside the function. Then point to the same array, so when the value of the slice is modified within the function, it will not affect the slice outside the function

Furthermore, we assign the slice to a larger cap, so that the len of the slice in the function will not exceed the cap after executing append, and observe whether the slice outside the function will change at this time

insert image description here

It can be seen that the assignment statement in the function successfully modifies the value of the slice outside the function at this time, because at this time, after the slice in the function executes append, the len of the slice does not exceed the cap, and no new array will be allocated, so the assignment will be performed later The statement modifies the array outside the function

おすすめ

転載: blog.csdn.net/shn111/article/details/128251512