Understanding slices in Go: an in-depth analysis of append operations (part 1)

Understanding the nature of slices in the Go language is very beneficial for programming. Below, I will use two code examples to explain the specific behavior of slices passed between different functions and perform append operations.

This article is the first article, when the slice capacity cap is sufficient

first code

slice1 has an initial length of 3 and a capacity of 10

func main() {
    
    
	slice1 := make([]int, 3, 10)
	fmt.Println("slice 1:", slice1, len(slice1), cap(slice1))

	test1(slice1)
  
	fmt.Println("slice 1:", slice1, len(slice1), cap(slice1))
  
  // 此时若访问 slice1[3] 则 panic,因为 len = 3,不可越界 len
  // fmt.Println( slice[3] )
}

func test1(slice2 []int) {
    
    
	// slice 2 对切片进行 append 操作
	slice2 = append(slice2, 1)
	fmt.Println("slice 2:", slice2, len(slice2), cap(slice2))
}

output:

slice 1[0 0 0] 3 10
slice 2[0 0 0 1] 4 10
slice 1[0 0 0] 3 10

We can observe that in the main function, the changes made by slice2 to the slice are not reflected in slice1, although they obviously operate on the same underlying array. Why is this so?

It turns out that in the above first code, slice2 = append(slice2, 1)this line of operation is executed inside the function test1, so it will not change the length of slice1 in the main function that calls this function len = 3, but only change the length of slice2 inside the test1 function len = 4. Therefore, in the main function, since the length of slice1 is still 3, we naturally cannot "see" the 4th element.

So what happens if we continue to append to slice1 in the main function? The answer is, it will directly overwrite the assignment to the 4th element in the test1 function! The specific situation is shown in the figure below:

slice append_flowchart

second code

With the above explanation, we can naturally write the following code to verify the above logic:

// 以下注释为执行时机,按顺序为 1 2 3:
func main() {
    
    
	slice1 := make([]int, 3, 10)
	fmt.Println("slice 1:", slice1, len(slice1), cap(slice1))

	go test1(slice1)
	time.Sleep(1 * time.Second)

	slice1 = append(slice1, 2) // 2

	fmt.Println("slice 1:", slice1, len(slice1), cap(slice1)) // 2

	time.Sleep(4 * time.Second)
}

func test1(slice2 []int) {
    
    
	slice2 = append(slice2, 1) // 1
	time.Sleep(2 * time.Second)
	fmt.Println("slice 2:", slice2, len(slice2), cap(slice2)) // 3
}

output:

slice 1[0 0 0] 3 10
slice 1[0 0 0 2] 4 10
slice 2[0 0 0 2] 4 10

in conclusion

  • When we pass slice1 to function B in function A and execute the append operation in B, as long as the underlying array is not expanded, it will append on the basis of the original array. At this time, the len in function B is 4.
  • Although the A and B functions share an underlying array, since the len of the A function remains 3, we cannot access the 4th element of the array, otherwise a panic will be raised.
  • When we also perform the append operation in the A function, the A function will directly overwrite the 4th digit value of the underlying array, thereby directly overwriting the value assigned by the B function.

Guess you like

Origin blog.csdn.net/qq_35760825/article/details/132272251