In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)05/31 Report--
This article mainly introduces "how to use Slice in Go language as a function parameter". In daily operation, I believe that many people have doubts about how to use Slice in Go language as a function parameter. Xiaobian consulted all kinds of materials and sorted out a simple and easy-to-use method of operation. I hope it will be helpful to answer the question of "how to use Slice in Go language as a function parameter". Next, please follow the editor to study!
Preface
First of all, it is important to make it clear that in the Go language, there is only value passing, reference passing and pointer passing are relative to parameter types.
Personally, I think the conclusion of the appeal is wrong. I regard the reference type as the encapsulation of the pointer, which is generally encapsulated as a structure, and the structure is a value type, so it feels like value transfer. Otherwise, I feel that the essence of other languages is not value transmission? However, I have just learned Go, and I may not fully understand it. If there is a problem, we can discuss it with each other.
Value types in the Go language: int, float, bool, array, sturct, etc. When a value type variable is declared, the compiler allocates a space in the stack where the value of the variable is stored.
Reference types in the Go language: slice,map,channel,interface,func,string, etc., declare a variable of the reference type, and the compiler allocates the memory of the instance on the heap.
String, like other languages, is a reference type, and the underlying implementation of string, struct String {byte* str; intgo len;}; but because string does not allow modification, each operation string can only generate a new object, so it looks like a value type when used.
In fact, a reference type can be seen as an encapsulation of a pointer.
Slice slicing is essentially a structural type in the GE language, which is defined in the source code as follows:
Source location: src/runtime/slice.go
Type slice struct {array unsafe.Pointer len int cap int}
From the definition, we can know that slice is a value type, array is the underlying array pointer, which points to the underlying allocated array; len is the number of elements of the underlying array; cap is the capacity of the underlying array, which will be expanded if it exceeds the capacity.
Problems and analysis
Typical problems
With the above knowledge laying the groundwork, let's take a look at the typical problem of passing slice as a function argument:
Package mainimport "fmt" func main () {tmp: = make ([] int, 0) fmt.Printf ("% p\ n", & tmp) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp) change (tmp) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp)} func change (tmp [] int) {fmt.Printf ("% p\ n") & tmp) tmp = append (tmp, 6) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp)} / / run result / / 0xc000004078// [] 00 0x59cde0//0xc0000040c0// [6] 1 1 0xc000014098// [] 00 0x59cde0
This is a typical question, the basic type of question for all your questions.
Question: isn't slice a reference type? When passing it as a parameter, the argument should be modified synchronously. Why is the tmp in the main function not changed?
Parsing:
From the previous knowledge, we already know that slice is essentially a structure, and when it is passed as a parameter, the parameter essentially copies the content of the entire structure of the argument, which is actually the value transfer.
The formal parameter allocates a memory space, which stores the same content as the actual parameter. From the running results, we can see that the memory address of the formal parameter and the actual parameter are different.
Because the pointer of the underlying array in the parameter is the same as the argument, it will be synchronously modified to the argument when you modify it. However, when you use the append function to add elements, the slice returned by the append function will overwrite the modification to the memory space of the parameter, independent of the argument, so the argument remains unchanged in the main function. You can see in the above code that the parameter in the function has changed but the argument has not changed.
Some students may have some questions after seeing the above analysis, such as:
The append function has a capacity expansion mechanism. When append is not expanded in the function, can elements be added to the argument synchronously?
Why is it that passing a pointer can be completely synchronized with the argument? isn't the pointer similar to a reference?
When append is used in the function, if the capacity is expanded, the address of the underlying array in the parameter memory space will be overwritten and modified to the address of the new expanded underlying array, while the argument remains unchanged. That's the code above.
Other questions 1
Package mainimport "fmt" func main () {tmp: = make ([] int, 0,5) tmp = append (tmp, 1,2,3) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp) change (tmp) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp)} func change (tmp [] int) {tmp = append (tmp) 4) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp)} / / [1 2 3] 3 5 0xc00000c300// [1 2 3 4] 4 5 0xc00000c300// [1 2 3] 3 5 0xc00000c300
Question: you can see from the code that there is no expansion when using append in the function, because the address of the underlying array in the parameter is the same as the argument, so why is there no element added to the argument?
Parsing:
In fact, the tmp [3] in the argument has become 4, but the len and cap in the memory space of the parameter and the parameter are independent. The len in the parameter is changed to 4, but the len in the argument is still 3, so no element is added in the argument.
The fact that tmp [3] has become 4 can be reflected in the following code:
Package mainimport "fmt" func main () {tmp: = make ([] int, 0,5) tmp = append (tmp, 1,2,3,4,5) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp) change (tmp [: 3]) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp)} func change (tmp [] int) {tmp = append (tmp) 6) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp)} / / [1 2 3 4 5] 5 5 0xc00000c300// [1 2 3 6] 4 5 0xc00000c300// [1 2 3 6 5] 5 0xc00000c300
It can be seen that 4 in the actual reference has become 6.
Or you can see it more directly from the following code:
Package mainimport ("fmt"unsafe") func main () {tmp: = make ([] int, 0,5) tmp = append (tmp, 1,2,3) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp)) Tmp) change (tmp) p: = unsafe.Pointer (& tmp [2]) Q: = uintptr (p) + 8t: = (* int) (unsafe.Pointer (Q)) fmt.Println (* t) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp)} func change (tmp [] int) {tmp = append (tmp, 4) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp) Tmp)} / / [1 2 3] 3 5 0xc00000c300// [1 2 3 4] 4 5 0xc00000c300//4// [1 2 3] 3 5 0xc00000c300
Move the address length of the element back to the address of the argument tmp [2] to get the address output of tmp [3], which can be seen to be 3.
Other questions 2
Package mainimport "fmt" func main () {tmp: = make ([] int, 0,5) tmp = append (tmp, 1,2,3) fmt.Printf ("% p\ n", & tmp) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp), tmp) change (& tmp) fmt.Printf ("% v% d% d% p\ n", tmp, len (tmp), cap (tmp) Tmp)} func change (tmp * [] int) {* tmp = append (* tmp, 4) fmt.Printf ("% p\ n", tmp) fmt.Printf ("% v% d% d% p\ n", * tmp, len (* tmp), cap (* tmp), * tmp)} / / 0xc000004078// [] 00 0xffdde0//0xc000004078// [1] 1 1 0xc000014098// [1] 1 0xc000014098
Question: why can the pointer be synchronously modified to the argument, * tmp = append (* tmp, 4). Isn't this also overwriting the modification to the parameter?
Parsing:
First of all, it is clear that when passing the pointer, the slice address is passed, and the parameter is the address rather than a memory space with the same content as the argument, as can be seen from the 0xc000004078 address printed in the code. So the code * tmp = append (* tmp, 4) overrides the slice pointed to by the address 0xc000004078, that is, the tmp slice in the main function, which can be seen from the change of the underlying array address of the slice tmp in the main function from 0xffdde0 to 0xc000014098.
At this point, the study of "how to use Slice in Go language as a function parameter" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
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.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.