The group quarreled again. Does go pass values or references?

Time:2021-12-2

Hello, I’m fried fish.

A few days ago, in our go communication group, a little partner asked the question “is XXX a reference type?” which triggered nearly 5 hours of discussion:

The group quarreled again. Does go pass values or references?

Turning back to the Nikkei issue, someone quarrels about it almost every month. namelyDo go languages pass values (value passing) or references (reference passing)

Go official definition

This part refers to “when are function parameters passed by value?” in the official go FAQ, as follows.

Like all languages in the C series,Everything in the go language is passed by value。 That is, a function always gets a copy of what is passed, just as an assignment statement assigns a value to a parameter.

The group quarreled again. Does go pass values or references?

For example:

  • Pass an int value to a function and you get a copy of int. Passing a pointer value will get a copy of the pointer, but will not get 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 it points to.
    • Copying an interface value copies what is stored in the interface value.
    • If the interface value holds a structure, copying the interface value copies the structure. If the interface value holds a pointer, copying the interface value copies the pointer, but also does not copy the data it points to.

The point is that everything in go language is value passing, and there is no reference passing. Don’t directly apply other concepts, you will make preconceived mistakes.

Value passing and reference passing

Value transmission

Pass by value is also called pass by value. hisIt refers to copying and passing a copy of the actual parameters to the function when calling the functionIn this way, if you modify the parameters in the function, the actual parameters will not be affected.

In short, value passing is a copy of the parameter, which is copied. In essence, it cannot be considered as a thing, and it does not point to a memory address.

Case 1 is as follows:

func main() {
    S: = "fried fish in your head"
    FMT. Printf ("main memory address:% P \ n", & S)
    hello(&s)
}

func hello(s *string) {
    FMT. Printf ("Hello memory address:% P \ n", & S)
}

Output results:

Main memory address: 0xc000116220
Hello memory address: 0xc000132020

We can see that the memory address pointed to by the variable s in the main function is0xc000116220。 After passing the parameters of the Hello function, the internal output memory address is0xc000132020, the two have changed.

The group quarreled again. Does go pass values or references?

According to this, we can conclude that all go languages are value passing. If you modify the value in the function, it will not affect the main function?

Case 2 is as follows:

func main() {
    S: = "fried fish in your head"
    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 is in your head"
}

We modified the value of variable s in the Hello function, so what is the value of variable s we output in the main function. Is it “fried fish in the brain” or “fried fish in the brain”?

Output results:

Main memory address: 0xc00000240
Hello memory address: 0xc000000e030
Fried fish is in my head

The output is “fried fish in the brain”. At this time, everyone may mutter again. The fried fish clearly said that the go language only has value transmission. It also verified that the memory addresses of the two are different. Why does his value change now? Why?

Because “if the passed value points to the address of the memory space, you can modify this memory space”.

That is, these two memory addresses are actually pointers to pointers, and their roots point to the same pointer, that is, to the variable s. Therefore, we further modify the variable s to get the result of “fried fish in the brain”.

Pass reference

Pass by reference is also called pass by reference,It refers to passing the address of the actual parameter directly to the function when calling the function, then the modification of parameters in the function will affect the actual parameters.

In the go language, the official has made it clear that there is no reference, that is, there is no reference.

Therefore, in the example, even if you pass in parameters, the final output memory address is the same.

The most controversial map and slice

At this time, some friends are confused. You see, the map and slice types in the go language can be modified directly. Isn’t it the same memory address or reference?

In fact, there is an important reminder in the FAQ: “the behavior of map and slice is similar to that of pointers. They are descriptors containing pointers to the underlying map or slice data”.

map

For map types, further expand to see examples:

func main() {
    m := make(map[string]string)
    M ["fried fish in your head"] = "this time!"
    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 ["fried fish in your head"] = "remember to praise!"
}

Output results:

Main memory address: 0xc000000e028
Hello memory address: 0xc000000e038

It is really value passing. What should be the result of the modified map. Since it’s value passing, it must be “this time!”, right?

Output results:

Map [fried fish in your head: remember to praise!]

The result is that the modification is successful and “remember to like!” is output. It’s embarrassing. Why is value passing, and it can also achieve the effect of similar reference, and can be modified to the source value?

Here’s the tip:

func makemap(t *maptype, hint int, h *hmap) *hmap {}

This is the underlying runtime method that creates the map type. Note that it returns*hmapType is a pointer. That is, go language encapsulates the relevant methods of map type to achieve the function that users need to pay attention to pointer passing.

That is, when we callhelloMethod, which is equivalent to passing in a pointer parameterhello(*hmap), similar to case 2 of the previous value type.

This kind of situation is called “reference type”, but “reference type” is not the same as passing a reference or passing a reference. There is still a clear difference.

In the go language, the Chan type is similar to the map type:

func makechan(t *chantype, size int) *hchan {}

The same effect.

slice

For slice types, further expand to see examples:

func main() {
    S: = [] string {"grilled fish", "salted fish", "fishing"}
    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 [0] = "fried fish"
}

Output results:

Main memory address: 0xc000098180
Hello memory address: 0xc000098180
[fried fish, salted fish, fishing]

From the results, the memory addresses of the two are the same, and they are successfully changed to the value of variable s. Isn’t this quote passing? The fried fish overturned?

Focus on two details:

  • of no avail&To get the address.
  • You can use it directly%pTo print.

The reason why the above two things can be done at the same time is because of the standard libraryfmtOptimized for this block:

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
    }

Notice the codevalue.Pointer, the standard library has carried out special processing to the pointer address of the directly corresponding value. Of course, there is no need to take the address character.

Standard libraryfmtThe reason why the value corresponding to slice type can be output is also here:

func (v Value) Pointer() uintptr {
    ...
    case Slice:
        return (*SliceHeader)(v.ptr).Data
    }
}

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

It is converted internallyDataProperty, which is the runtime representation of slice type in go language, sliceheader. We’re calling%pWhen outputting, the address of the array element is stored at the bottom of the output slice.

The next question is: why can slice type directly modify the value of source data.

In fact, the principle of output is the same. When the go language runs, it also passes the pointer of the underlying array of the corresponding slice type, but it should be noted that it uses a copy of the pointer. Strictly speaking, reference type is still value passing.

Good or not?

summary

In today’s article, we have made a basic explanation and Analysis on the Nikkei problem of go language: “whether go language is value passing (value passing) or reference passing (reference passing)”.

In addition, in the industry, the most confused types are slice, map, Chan and so on, which will be regarded as “reference transmission”, so it is considered that XXX in go language is reference transmission. We also have a case demonstration.

In fact, this is not a correct understanding, because: “if the passed value points to the address of the memory space, you can modify this memory space”.

It does copy a copy, but it also uses various means (actually passing pointers) to achieve the effect of modifying the source data. It is a reference type.

Stone hammer, go language has only value transmission,

If you have any questions, you are welcome to feedback and exchange in the comment area,The best relationship is mutual achievement, everybodygive the thumbs-upnamelyFried fishThe greatest driving force of creation, thank you for your support.

The article is continuously updated. You can search [fried fish in your brain] on wechat to read this articleGitHub github.com/eddycjy/blogHas been included, welcome star reminder. You can add my wechatcJY0728, I’ll pull you into the go reader exchange group and exchange technology with thousands of developers.

reference resources