For understanding a language, we will be concerned about whether the parameters are passed values or references when we call a function?
In fact, value transfer and reference transfer are an old topic. Researchers have this concept, but it may not be very clear. For those of us who do Go language development, we also want to know what transmission is.
Let's first look at what is value passing and what is reference passing.
####What is value passing (value passing)
Value passing means that the function always passes a copy of the original thing. For example, when we pass an int type parameter, we actually pass a copy of the parameter; When you pass a pointer type parameter, you actually pass a copy of the pointer, not the value pointed to by the pointer.
For basic types such as int, we can well understand that they are a copy, but what about pointers? We think we can modify the original value through it. How can it be a copy?
Let's take an example.
func main() { i:=10 ip:=&i fmt.Printf("The memory address of the original pointer is:%p\n",&ip) modify(ip) fmt.Println("int The value has been modified and the new value is:",i) } func modify(ip *int){ fmt.Printf("The memory address of the pointer received in the function is:%p\n",&ip) *ip=1 }
When we run, we can see the input results as follows:
The memory address of the original pointer is 0xc4200c028
The memory address of the pointer received in the function is 0xc4200c038
The int value has been modified and the new value is: 1
First of all, we should know that anything stored in memory has its own address, and the pointer is no exception. Although it points to other data, it also has memory for storing the pointer.
Therefore, through the output, we can see that this is a copy of the pointer, because the memory addresses of the two pointers are different. Although the values of the pointers are the same, they are two different pointers.
Through the above figure, we can better understand. First, we see that we declare a variable i with a value of 10 and its memory address is 0xc420018070. Through this memory address, we can find the variable i, which is the pointer ip of the variable i.
Pointer ip is also a pointer type variable. It also needs memory to store it. What is its memory address? It's 0xc4200c028. When we pass the pointer variable ip to the modify function, it is a copy of the pointer variable, so the memory address of the newly copied pointer variable ip has changed and is a new 0xc4200c038.
Whether 0xc4200c028 or 0xc4200c038, we can call them pointers to pointers. They point to the same pointer 0xc420018070, which in turn points to variable i, which is why we can modify the value of variable i.
What is reference passing (reference passing)
The Go language (Golang) is not passed by reference. I can't use the Go example here, but it can be described by description.
Taking the above example as an example, if the memory address printed in the modify function is unchanged, which is 0xc4200c028, then it is reference passing.
Confused Map
We have understood the value passing and reference passing, but we may still feel confused about the Map type. First, we can modify its content through methods, and second, it has no obvious pointer.
func main() { persons:=make(map[string]int) persons["Zhang San"]=19 mp:=&persons fmt.Printf("original map The memory address of is:%p\n",mp) modify(persons) fmt.Println("map The value has been modified and the new value is:",persons) } func modify(p map[string]int){ fmt.Printf("Received in function map The memory address of is:%p\n",&p) p["Zhang San"]=20 }
Run printout:
The memory address of the original map is 0xc4200c028
The memory address of the map received in the function is 0xc4200c038
The map value has been modified. The new value is: map [Zhang San: 20]
The two memory addresses are different, so this is a value transfer (copy of value), so why can we modify the contents of the Map? Don't worry, let's look at a struct implemented by ourselves.
func main() { p:=Person{"Zhang San"} fmt.Printf("original Person The memory address of is:%p\n",&p) modify(p) fmt.Println(p) } type Person struct { Name string } func modify(p Person) { fmt.Printf("Received in function Person The memory address of is:%p\n",&p) p.Name = "Li Si" }
Run printout:
The memory address of the original Person is 0xc4200721b0
The memory address of Person received in the function is 0xc4200721c0
{Zhang San}
We found that the Person type defined by ourselves is also value passed when the function passes parameters, but its value (Name field) has not been modified. We want to change it to Li Si. We found that the final result is Zhang San.
In other words, the map type is different from the struct type defined by ourselves. We try to change the receive parameter of the modify function to the pointer of Person.
func main() { p:=Person{"Zhang San"} modify(&p) fmt.Println(p) } type Person struct { Name string } func modify(p *Person) { p.Name = "Li Si" }
After running and viewing the output, we found that it was modified this time. We omit the printing of memory address here, because our example of int type above has proved that the parameter of pointer type is also passed by value.
The pointer type can be modified, but not the non pointer type. So we can boldly guess whether the map we created with the make function is a pointer type?
Take a look at the source code:
// makemap implements a Go map creation make(map[k]v, hint) // If the compiler has determined that the map or the first bucket // can be created on the stack, h and/or bucket may be non-nil. // If h != nil, the map can be created directly in h. // If bucket != nil, bucket can be used as the first bucket. func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap { //Omit irrelevant code }
By looking at Src / Runtime / HashMap Go source code found that, as we guessed, the make function returns a pointer * hmap of type hmap. That is, map===*hmap. Now, a function like func modify(p map) is actually equal to func modify(p *hmap), which is the same as the example of func modify(ip *int) in the first section of what is value transfer. You can refer to the analysis.
So here, the Go language saves the pointer operation for us through the make function and literal packaging, so that we can use the map more easily. The map here can be understood as reference type, but remember that reference type is not passed reference.
chan type
chan Type is essentially the same as map The type is the same. I won't introduce it too much here. Please refer to the source code: func makechan(t *chantype, size int64) *hchan { //Omit irrelevant code }
chan is also a reference type, just like map. make returns a * hchan.
slice different from map and chan
slice is different from map and chan. The same thing is that it is also a reference type. It can also modify the corresponding content in the function.
func main() { ages:=[]int{6,6,6} fmt.Printf("original slice The memory address is%p\n",ages) modify(ages) fmt.Println(ages) } func modify(ages []int){ fmt.Printf("Received in function slice The memory address is%p\n",ages) ages[0]=1 }
Run the print result and find that it has indeed been modified, and the memory address of slice printed here can be directly printed through% p without using & address character conversion.
Can this prove that make slice is also a pointer? Not necessarily. Maybe FMT Printf made slice special.
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 } //Omit some codes }
It is found through the source code that chan, map, slice, etc. are treated as pointers through value Pointer() gets the pointer of the corresponding value.
// If v's Kind is Slice, the returned pointer is to the first // element of the slice. If the slice is nil the returned value // is 0. If the slice is empty but non-nil the return value is non-zero. func (v Value) Pointer() uintptr { // TODO: deprecate k := v.kind() switch k { //Omit irrelevant code case Slice: return (*SliceHeader)(v.ptr).Data } }
Obviously, when it is slice type, the return is the address of the first element of the field Data in the slice structure.
type SliceHeader struct { Data uintptr Len int Cap int } type slice struct { array unsafe.Pointer len int cap int }
Therefore, the address of the slice variable ages printed through%p is actually the address of the internal storage array element. Slice is a mixed type of structure + element pointer. The purpose of modifying the elements stored in slice can be achieved through the pointer of element array(Data).
Therefore, there are many ways to modify the content of a type. The type itself can be used as a pointer, and the fields with pointer types in the type can also be used.
Simply from the slice structure, we can modify the contents of storage elements through modify, but we can never modify len and cap, because they are only a copy. If you want to modify, you need to pass * slice as a parameter.
func main() { i:=19 p:=Person{name:"Zhang San",age:&i} fmt.Println(p) modify(p) fmt.Println(p) } type Person struct { name string age *int } func (p Person) String() string{ return "Name:" + p.name + ",Age:"+ strconv.Itoa(*p.age) } func modify(p Person){ p.name = "Li Si" *p.age = 20 }
The printout result of the operation is:
Name: Zhang San, age: 19
Name: Zhang San, age: 20
Through the comparison between Person and slice, we can better understand that the name field of Person is similar to the len and cap fields of slice, and the age field is similar to the array field. When the parameter passed is of non pointer type, only the age field can be modified, and the name field cannot be modified. To modify the name field, change the passed parameter to a pointer, for example:
modify(&p) func modify(p *Person){ p.name = "Li Si" *p.age = 20 }
In this way, both the name and age fields have been modified.
So slice type is also a reference type.
Summary
Finally, we can confirm that all parameters passed in Go language are value passing (value passing), which is a copy and a copy. Because the copied content is sometimes of non reference type (int, string, struct, etc.), the original content data cannot be modified in the function; There are reference types (pointer, map, slice, chan, etc.), so that the original content data can be modified.
Whether the original content data can be modified is not necessarily related to value transfer and reference transfer. In C + +, the original content data can be modified by passing reference. In Go language, although only passing value, we can also modify the original content data because the parameter is a reference type.
Also remember that reference type and reference passing are two concepts. Remember that there is only value passing (value passing) in Go.