Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the hidden crises transmitted by slices?

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "what are the hidden crises of slice transmission". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought. Let's study and learn "what are the hidden crises of slice transmission"?

In Go's source code library or other open source projects, you will find that when you need to use slice input parameters, it uses a pointer to the slice type instead of the slice type. There is a question here: isn't the bottom of the slice a pointer to the underlying array data? why not pass the slice directly? what's the difference between the two?

For example, in the source log package, the formatHeader method is bound to the Logger object, and its input object, buf, is of type * [] byte, not [] byte.

1func (l * Logger) formatHeader (buf * [] byte, t time.Time, file string, line int) {}

There are the following examples

1func modifySlice (innerSlice [] string) {

2 innerSlice [0] = "b"

3 innerSlice [1] = "b"

4 fmt.Println (innerSlice)

5}

six

7func main () {

8 outerSlice: = [] string {"a", "a"}

9 modifySlice (outerSlice)

10 fmt.Print (outerSlice)

11}

twelve

13max / output is as follows

14 [b b]

15 [b b]

We change the input parameter type of the modifySlice function to a pointer to the slice

1func modifySlice (innerSlice * [] string) {

2 (* innerSlice) [0] = "b"

3 (* innerSlice) [1] = "b"

4 fmt.Println (* innerSlice)

5}

six

7func main () {

8 outerSlice: = [] string {"a", "a"}

9 modifySlice & outerSlice)

10 fmt.Print (outerSlice)

11}

twelve

13max / output is as follows

14 [b b]

15 [b b]

In the above example, the results of the two function parameter passing types are the same, and there seems to be no difference. Passing it through a pointer seems useless, and in any case the slice is passed by reference, and in both cases the contents of the slice have been modified.

This confirms our consistent understanding that the modification of slices within the function will affect the slices outside the function. But is that really the case?

Textual research and explanation

In the article "do you really understand the conversion between string and [] byte", we talked about the underlying structure of the slice as follows.

1type slice struct {

2 array unsafe.Pointer

3 len int

4 cap int

5}

Array is the pointer to the underlying array, len is the length, and cap is the capacity.

We make the following minor changes to the example above.

1func modifySlice (innerSlice [] string) {

2 innerSlice = append (innerSlice, "a")

3 innerSlice [0] = "b"

4 innerSlice [1] = "b"

5 fmt.Println (innerSlice)

6}

seven

8func main () {

9 outerSlice: = [] string {"a", "a"}

10 modifySlice (outerSlice)

11 fmt.Print (outerSlice)

12}

thirteen

14max / output is as follows

15 [b b a]

16 [an a]

Something amazing happened, and the modification to the slice in the function failed to affect the external slice.

To clearly understand what happened, add more details to the print.

1func modifySlice (innerSlice [] string) {

2 fmt.Printf ("% p% v% p\ n", & innerSlice, innerSlice, & innerSlice [0])

3 innerSlice = append (innerSlice, "a")

4 innerSlice [0] = "b"

5 innerSlice [1] = "b"

6 fmt.Printf ("% p% v% p\ n", & innerSlice, innerSlice, & innerSlice [0])

7}

eight

9func main () {

10 outerSlice: = [] string {"a", "a"}

11 fmt.Printf ("% p% v% p\ n", & outerSlice, outerSlice, & outerSlice [0])

12 modifySlice (outerSlice)

13 fmt.Printf ("% p% v% p\ n", & outerSlice, outerSlice, & outerSlice [0])

14}

fifteen

16max / output is as follows

170xc00000c060 [an a] 0xc00000c080

180xc00000c0c0 [an a] 0xc00000c080

190xc00000c0c0 [b b a] 0xc000022080

200xc00000c060 [an a] 0xc00000c080

In the Go function, the argument of the function is passed by value. So, passing the slice to the function by argument is essentially a copy of the slice structure object, and the field values of both slice structures are equal. Normally, because the array of the slice structure in the function and the array of the slice structure outside the function point to the same underlying array, both of them will be affected when the data in the underlying array is modified.

However, there is such a problem: if the pointer to the underlying array is overwritten or modified (copy, redistribution, append triggers capacity expansion), the modification of the data inside the function will no longer affect the external slices, and the len and capacity cap representing the length will not be modified.

In order to make the reader more aware of this, the above process is visualized as follows.

As you can see, when the length and capacity of the slice are equal, append occurs, and the expansion of the slice is triggered. When the capacity is expanded, a new underlying array is created, the data from the original array is copied to the new array, and the additional data is placed in the new array. The array pointer to the slice points to the new underlying array. Therefore, the relationship between the in-function slice and the out-of-function slice has been completely cut off, and its change has no effect on the out-of-function slice.

Note that slice expansion is not always equal to multiple expansion. To avoid misunderstanding, here is a brief description of the principle of slice expansion (the source code is located in the growslice function in src/runtime/slice.go):

When the slice is expanded, when the required capacity is more than twice the capacity of the original slice, the required capacity will be directly used as the new capacity. Otherwise, when the length of the original slice is less than 1024, the capacity of the new slice will directly double. When the capacity of the original slice is greater than or equal to 1024, it will be increased by 25% repeatedly until the new capacity exceeds the required capacity.

At this point, we finally know why some functions need to use a pointer to the slice type instead of the slice type when using slice input parameters.

1func modifySlice (innerSlice * [] string) {

2 * innerSlice = append (* innerSlice, "a")

3 (* innerSlice) [0] = "b"

4 (* innerSlice) [1] = "b"

5 fmt.Println (* innerSlice)

6}

seven

8func main () {

9 outerSlice: = [] string {"a", "a"}

10 modifySlice (& outerSlice)

11 fmt.Print (outerSlice)

12}

thirteen

14max / output is as follows

15 [b b a]

16 [b b a]

Remember, if you only want to modify the value of the elements in the slice without changing the capacity and direction of the slice, you can pass the slice by value, otherwise you should consider passing it by pointer.

Example consolidation

In order to determine whether the reader has really understood the above question, I have made two variants of the above example, which readers can test themselves.

Test one

1func modifySlice (innerSlice [] string) {

2 innerSlice [0] = "b"

3 innerSlice = append (innerSlice, "a")

4 innerSlice [1] = "b"

5 fmt.Println (innerSlice)

6}

seven

8func main () {

9 outerSlice: = [] string {"a", "a"}

10 modifySlice (outerSlice)

11 fmt.Println (outerSlice)

12}

Test two

1func modifySlice (innerSlice [] string) {

2 innerSlice = append (innerSlice, "a")

3 innerSlice [0] = "b"

4 innerSlice [1] = "b"

5 fmt.Println (innerSlice)

6}

seven

8func main () {

9 outerSlice:= make ([] string, 0,3)

10 outerSlice = append (outerSlice, "a", "a")

11 modifySlice (outerSlice)

12 fmt.Println (outerSlice)

13}

Test one answer

1 [b b a]

2 [b a]

Test 2 answer

1 [b b a]

2 [b] Thank you for your reading. The above is the content of "what is the hidden crisis of slice transmission". After the study of this article, I believe you have a deeper understanding of the hidden crisis of slice transmission, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report