Hello, I’m fried fish.
A few days ago in our Go communication group, a small partner asked “is XXX a reference type?” This question, which led to nearly five hours of discussion:
Back to the Nikkei, which hardly a month goes by without a row. Is the Go language passed by value (value passed) or by reference (reference passed)?
Go official definition
This part refers to “When are function parameters passed by value?” in the Go official FAQ. , as follows.
As with all languages in the C series, everything in Go is passed by value. That is, a function always gets a copy of what is passed, just as there is an assignment statement that assigns values to arguments.
Such as:
-
Passing an int to a function yields a copy of int.
Passing a pointer gets a copy of the pointer, but not the data it points to.
-
Map and slice behave like Pointers: they are descriptors that contain Pointers to the underlying map or slice data.
-
Copying a map or Slice value does not copy the data to which it points.
-
Copying an interface value copies what is stored in the interface value.
-
If the interface value holds a structure, copying the interface value copies that structure. If the interface value holds a pointer, copying the interface value copies that pointer, but also not the data to which it points.
Just to be clear, everything in Go is passed by value, not by reference. Don’t directly set up other concepts, you will make a preconceived mistake.
Pass values and references
The value of
Pass by value is also called pass by value. This means that when a function is called, a copy of the actual parameters is passed to the function, so that if the parameters are changed in the function, the actual parameters will not be affected.
In simple terms, value passing is passing a copy of the parameter, a copy of it, not a thing per se, pointing to a memory address.
Case one is as follows:
Func main() {s := "FMT.Printf("main: %p\n", &s) hello(&s)} func hello(s *string) {FMT.Printf(" FMT. %p\n", &s) }Copy the code
Output result:
Main: 0xC000116220 Hello: 0xC000132020Copy the code
We can see that the variable S in the main function points to the memory address 0xC000116220. After passing an argument to the Hello function, it internally outputs the memory address 0xC000132020, and the two have changed.
From this we can conclude that the Go language is really all value passing. Can I change the value inside the function without affecting main?
Case two is as follows:
Func main() {s := "FMT.Printf("main memory address: %p\n", &s) hello(&s) fmt.println (s)} func hello(s *string) {fmt.printf ("hello memory address: %p\n", &s) *s = "fried fish in my head"}Copy the code
We changed the value of s in hello, so what is the value of s in main? Brain goes into fried fish, or fish goes into brain?
Output result:
Main memory address: 0xC000010240 Hello memory address: 0xC00000e030 Fried fish into the brainCopy the code
The output is “Fried fish in the brain.” At this time, you may be wondering again, before frying fish clearly said that Go language only value transfer, also verified the memory address of the two are not the same, how he now his value changed, why?
Because “if the passed value points to the address of the memory space, then the memory space can be modified”.
So these two memory addresses, they’re actually Pointers to Pointers, and they both point to the same pointer, which is the variable S. So we further modify the variable S to get the output “fried fish into the brain”.
The reference
Pass by reference refers to passing the address of the actual parameter to the function directly when calling the function, so that the modification of the parameter in the function will affect the actual parameter.
In Go, it is officially stated that no reference is passed, i.e., no reference is passed.
So using words to make things simple, like in the example, even if you pass in the parameter, the final output is the same memory address.
The most controversial are Map and Slice
The map and slice types in Go can be modified directly. The map and slice types can be modified directly.
It is important to note in the FAQ that “Maps and slices behave like Pointers, which are descriptors containing Pointers to underlying map or slice data”.
map
For the map type, expand further to see an example:
Func main() {m := make(map[string]string) m FMT.Printf("main memory address: %p\n", &m) hello(m) fmt.printf ("%v", m)} func hello(p map[string]string) {fmt.printf ("hello memory address: %p\n", &p) p[" brain fried fish "] = "Remember to like!" }Copy the code
Output result:
Main memory address 0xC00000e028 Hello memory address 0xC00000e038Copy the code
It is true that values are passed, so what should be the result of the modified map? Since it’s value passing, it must be “This time!” , right?
Output result:
Map [Brain into fish fry: remember to like!Copy the code
The result is a successful modification, and the output “Remember to like!” . This is embarrassing, why is the value pass, but also can achieve the effect of reference, can modify to the source value?
Here are the tips:
func makemap(t *maptype, hint int, h *hmap) *hmap {}
Copy the code
This is the low-level Runtime method that creates the map type. Note that it returns the *hmap type, which is a pointer. In other words, the Go language encapsulates relevant methods of map type, so that users need to pay attention to pointer passing.
This means that when we call hello, we are passing in a pointer argument hello(*hmap), similar to the previous case 2 of value type.
Such cases are called “reference types”, but “reference types” are not the same as passing a reference or passing a reference, but there is a clear distinction.
Similar to the map type in the Go language are the chan types:
func makechan(t *chantype, size int) *hchan {}
Copy the code
Same effect.
slice
For slice, let’s expand to see an example:
Func main() {s := []string{" FMT ", "FMT "," FMT "} FMT. %p\n", s) hello(s) FMT.Println(s)} func hello(s []string) {FMT.Printf("hello memory address: %p\n", s) s[0] = "fish"}Copy the code
Output result:
Hello memory address: 0xC000098180Copy the code
From the result, the memory address of the two is the same, and the value of the variable S has been successfully changed. Isn’t that pass-by, the fried fish tipping over?
Focus on two details:
-
There’s no ampersand to get the address.
-
You can print directly with %p.
It is possible to do both because the standard library FMT is optimized for this area:
func (p *pp) fmtPointer(value reflect.Value, verb rune) {
var u uintptr
switch value.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:
u = value.Pointer()
default:
p.badVerb(verb)
return
}
Copy the code
Note the code value.Pointer; / / the library does something special so that the address of the Pointer directly corresponds to the value does not need to be fetched.
This is why the library FMT is able to output values corresponding to slice types:
func (v Value) Pointer() uintptr {
...
case Slice:
return (*SliceHeader)(v.ptr).Data
}
}
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
Copy the code
Its internally converted Data property is exactly the runtime representation of the Slice header in the Go language. When we call the %p output, we store the address of the array element at the bottom of the output slice.
The next question is why the slice type can modify the source data directly.
In the same way as output, Go passes a pointer to the underlying array of the corresponding slice type, but it is important to note that it uses a copy of the pointer. Strictly a reference type, still a value pass.
Better not?
conclusion
In this article today, we have carried on the basic explanation and analysis for the nikkei question of Go language: “Is Go language pass value (value pass), or pass reference (reference pass)”.
In addition, in the industry, most people are confused about the types of Slice, map, chan and so on. They all think that they are “referential pass”, so they think that XXX of Go language is referential pass. We have also carried out case demonstration on this.
In fact, this is not correct because: “if the value passed is to the address of the memory space, the memory space can be modified”.
It does make a copy, but it also achieves the effect of modifying the source data by various means (namely, passing Pointers), which is the reference type.
Stone hammer, Go language only has value transfer,
If you have any questions, welcome feedback and communication in the comments section. The best relationship is mutual achievement. Your praise is the biggest motivation for the creation of fried fish, thank you for your support.
This article is constantly updated. You can read it on wechat by searching “Brain into fried fish”. GitHub github.com/eddycjy/blo… Star has been included, welcome to urge more.
reference
-
Go Reader exchange group
-
When are function parameters passed by value?
-
Is Java passed by value or by reference?
-
Go language parameters are passed by value or by reference