For the understanding of a language, will care about when we call the function, the parameter is the value of the pass, or reference?
In fact, for the transfer of value and reference, is a relatively old topic, do research and development have this concept, but may not be very clear. For us to do the go language development, but also want to know exactly what is passed.
So let's take a look at what is value passing and what is reference passing.
First, what is the value transfer (value pass)
The value of the pass means that the function is always passing a copy of this thing and a copy of the original. For example, if we pass an int parameter, it is actually a copy of the parameter, and passing a pointer-type parameter actually passes a copy of the pointer, not the value that the pointer points to.
For basic types such as int we can understand very well that they are a copy, but what about pointers? We think we can modify the original value by it, how can it be a copy? Let's look at an example below.
func main() {i:=10ip:=&ifmt.Printf("原始指针的内存地址是:%p\n",&ip)modify(ip)fmt.Println("int值被修改了,新值为:",i)}func modify(ip *int){fmt.Printf("函数里接收到的指针的内存地址是:%p\n",&ip)*ip=1}
We run and can see the following input results:
原始指针的内存地址是:0xc42000c028函数里接收到的指针的内存地址是:0xc42000c038int值被修改了,新值为: 1
The first thing we need to know is that anything stored in memory has its own address, and the pointer is no exception, although it points to other data, but there is also memory that holds the pointer.
So through the output we can see that this is a copy of the pointer, because the memory address that holds the two pointers is different, although the value of the pointer is the same, but it is two different pointers.
Pointer Pass Interpretation
Through the above diagram, you can better understand.
First we see that we declare a variable i value of 10, its memory storage address is 0xc420018070, through this memory address, we can find the variable i, this memory address is the variable i pointer ip.
The pointer IP is also a pointer-type variable, it also needs memory to store it, its memory address is how much? It's 0xc42000c028.
When we pass the pointer variable IP to the Moditify function, it is the copy of the pointer variable, so the new copy of the pointer variable IP, its memory address has changed, is the new 0xc42000c038.
Whether it's 0xc42000c028 or 0xc42000c038, we can call pointer pointers, they point to the same pointer 0xc420018070, and the 0xc420018070 points to the variable I, which is why we can modify the value of the variable i.
Ii. What is a pass-through reference (reference pass)
The Go Language (Golang) is not quoted, and I can't use go for example, but it can be described by description.
In the example above, if the memory address printed in the Moditify function is constant and 0xc42000c028, then the reference is passed.
2.1 Confusing Map
Understand the value of the pass and the reference, but for the map type, it may be confusing, one can modify its contents by means of the method, and it has no obvious pointer.
func main() {persons:=make(map[string]int)persons["张三"]=19mp:=&personsfmt.Printf("原始map的内存地址是:%p\n",mp)modify(persons)fmt.Println("map值被修改了,新值为:",persons)}func modify(p map[string]int){fmt.Printf("函数里接收到map的内存地址是:%p\n",&p)p["张三"]=20}
Run Print output:
原始map的内存地址是:0xc42000c028函数里接收到map的内存地址是:0xc42000c038map值被修改了,新值为: map[张三:20]
Two memory addresses are not the same, so this is another value pass (a copy of the value), so why can we modify the contents of the map? First of all, let's look at a struct of our own realization.
func main() {p:=Person{"张三"}fmt.Printf("原始Person的内存地址是:%p\n",&p)modify(p)fmt.Println(p)}type Person struct {Name string}func modify(p Person) {fmt.Printf("函数里接收到Person的内存地址是:%p\n",&p)p.Name = "李四"}
Run Print output:
原始Person的内存地址是:0xc4200721b0函数里接收到Person的内存地址是:0xc4200721c0{张三}
We find that the person type we define ourselves is also a value pass when the function is passed, but its value (the Name field) has not been modified, we want to change to John Doe, and we find that the final result is still Zhang San.
This means that the map type is not the same as the struct type we define ourselves. We tried to change the receive parameter of the Modify function to the person's pointer.
func main() {p:=Person{"张三"}modify(&p)fmt.Println(p)}type Person struct {Name string}func modify(p *Person) {p.Name = "李四"}
After running the view output, we found that this time it was modified. We omit the printing of the memory address here, because the example of the int type above has proved that the parameter of the pointer type is also the value passed.
Pointer type can be modified, non-pointer type No, then we can boldly guess, we use the Make function to create a map is not 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 {//省略无关代码}
By looking at Src/runtime/hashmap.go source code discovery, it is true that, as we guessed, the Make function returns a pointer *hmap of type HMAP. That means map===*hmap.
Now look at the func modify (P map) function, which is actually equal to the Func modify (P *hmap), and we can refer to the analysis in the same way as the example of the func modify (IP *int) in the first section above what is value passing .
So here, the go language through make function, the literal packaging, for us to omit the operation of the pointer, so that we can more easily use map. The map here can be understood as a reference type, but remember that the reference type is not a reference.
2.2 Chan Type
Chan type is essentially the same as the map type, here do not do too much introduction, refer to the following source code:
func makechan(t *chantype, size int64) *hchan {//省略无关代码}
Chan is also a reference type, similar to map, and make returns a *hchan.
2.3 and map, Chan are not the same slice
Slice and map, Chan are not the same, the same is, it is a reference type, it can also modify the corresponding content in the function.
func main() {ages:=[]int{6,6,6}fmt.Printf("原始slice的内存地址是%p\n",ages)modify(ages)fmt.Println(ages)}func modify(ages []int){fmt.Printf("函数里接收到slice的内存地址是%p\n",ages)ages[0]=1}
Run the print results, the discovery is indeed modified, and we here print slice memory address can be printed directly through%p, do not use the & address character conversion.
This proves that make's slice is also a pointer? Not necessarily, may also be FMT. printf gave slice special treatment.
func (p *pp) fmtPointer(value reflect.Value, verb rune) {var u uintptrswitch value.Kind() {case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Slice, reflect.UnsafePointer:u = value.Pointer()default:p.badVerb(verb)return}//省略部分代码}
Through source code discovery, for Chan, map, slice, etc. are treated as pointers, through value. Pointer () Gets a pointer to 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: deprecatek := v.kind()switch k {//省略无关代码case Slice:return (*SliceHeader)(v.ptr).Data}}
obviously, when it was Slice type, the return is Slice in this struct, the address of the first element of the field data.
type SliceHeader struct {Data uintptrLen intCap int}type slice struct {array unsafe.Pointerlen intcap int}
So the address of the slice variable ages that we print through%p is actually the address of the internal storage array element, and slice is a mixed type of struct + element pointer, which can be used to modify the storage element in slice by a pointer to the element array (Data).
So there are many ways to modify the content of a type, and the type itself can be a pointer, and a field of type with a pointer type can.
Purely from the slice of this structure, we can modify the contents of the storage elements through modify, but never change the Len and cap, because they are just a copy, if you want to modify it, it is necessary to pass the *slice as a parameter.
func main() {i:=19p:=Person{name:"张三",age:&i}fmt.Println(p)modify(p)fmt.Println(p)}type Person struct {name stringage *int}func (p Person) String() string{return "姓名为:" + p.name + ",年龄为:"+ strconv.Itoa(*p.age)}func modify(p Person){p.name = "李四"*p.age = 20}
Run print output as:
姓名为:张三,年龄为:19姓名为:张三,年龄为:20
By comparing this person to slice, it is better to understand that the person's name field is similar to the slice Len and Cap fields, and the age field is similar to the array field. In the case of a non-pointer type, only the age field can be modified, and the Name field cannot be modified. To modify the Name field, change the parameter to a pointer, for example:
modify(&p)func modify(p *Person){p.name = "李四"*p.age = 20}
Both the name and age fields have been modified.
So the slice type is also a reference type.
Summary
Finally, we can confirm that all the arguments in the go language are value passes (values), a copy, and a copy. Because the contents of the copy are sometimes non-reference types (int, string, struct, etc.), the original content data cannot be modified in the function, some reference types (pointers, maps, Slice, chan, etc.) so that the original content data can be modified.
Whether the original content data can be modified, and the value of the transfer, the reference does not necessarily have a relationship. In C + +, the reference must be able to modify the original content data, in the go language, although only the value, but we can also modify the original content data, because the parameter is a reference type.
Also remember that reference types and pass-through references are two concepts.
Remember that there is only a pass value (value pass) in go.