How can Go have a quote? Make (); make(); make(); make();

I say: is that a quote? Are you sure what a quote is? If you’re confused, listen to me:

It all starts with variables.

What is a variable

Whether a reference variable or a pointer variable is a variable; So, what is a variable? A variable is essentially a piece of memory. In general, the most direct way to operate on computer memory is to say, “Computer, store an integer 100 at address 0x0201, store a floating point 10.6 at address 0x00202, read 0x00203…” This is ok for the machine to operate, but if it were written directly in code for people to see, this pile of “0x0201, 0x0202… So clever people have come up with a way to represent a bunch of difficult addresses in a way that other humans can easily read. For example, name the address 0x0201 as ID and 0x0202 as Score… Then, during the compilation of the code, the human-readable text such as “name” is converted into a real memory address. Thus, the variable was born ~

So, each variable represents a block of memory, the variable name is an alias we give to that block of memory, and the value in memory is the value we assign to the variable. Variable names are directly converted to memory addresses during program compilation.

What is a quote

A reference is a variable that points to another variable, or an alias for a known variable.

Note that the reference corresponds to the same memory address as the variable to which the reference itself refers. The reference itself is also converted to a real memory address during compilation. Of course, the reference and the variable it points to are converted to the same memory address during compilation.

What is a pointer

The pointer itself is also a variable and needs to allocate a memory address, but the memory address contains the memory address of another variable. A little wound, please look at the picture:

References and Pointers in GO

Let’s first look at an example of a “proper” reference in C++ :

#include <stdio.h>

int main(void)
{

        int i = 3;
        int *ptr = &i;
        int &ref = i;

        printf("%p %p %p\n", &i, ptr, &ref); 
        // Print: 0x7ffeeAC553a8 0x7FFEEAC553a8 0x7FfEEAC553a8
        return 0;
}
Copy the code

Variable address, reference address, pointer value are the same; Common sense

Let’s try another example of similar code in Go:

package main

import "fmt"

func main(a) {
    i := 3
    ref := i
    ptr := &i
    
    fmt.Println(fmt.Sprintf("%p %p %p", &i, &ref, ptr))
    // Print 0xC000118000 0xC000118008 0xC000118000
}
Copy the code

The address of the variable I is the same as the value of the pointer PTR, as expected; But: just as there is no special “reference symbol” in Go (int &ref = I; in C++) Again, the ref in the go code above is simply a variable, not a reference at all.

However, a lot of people do not give up, is “the test subject” wrong? We use int in our code, but we can use slice and map instead. After all, this is what the “profile” looks like online: here’s a screenshot (just look at the red) :

Here’s a screenshot (just look at the red) :

Ok, so we can try the following map code and see if there is a reference at the bottom:

package main

import "fmt"

func main(a){
    i := make(map[string]string)
    i["key"] ="value"

    ref := i

    fmt.Println(fmt.Sprintf("%p %p", &i, &ref))
    // Print: 0xC00010e018 0xC00010E020
}
Copy the code

Ha ha! No, if it is a quote, the printed address should be the same, but now it is not! So it doesn’t exist? Don’t worry, let’s look at the following example:

package main

import "fmt"

func main(a){
    i := make(map[string]string)
    i["key"] ="value"

    ref := i
    ref["key"] = "value1"

    fmt.Println(i["key"]) // Print the result: value1
    fmt.Println(ref["key"]) // Print the result: value1

    fmt.Println(fmt.Sprintf("%p %p", &i, &ref))
    // Print the result: 0xC00000E028 0xC00000E030
}
Copy the code

Can you guess what was printed? The address of the variable is wrong, but, but the value has changed! The ref variable can “manipulate” the contents of the I variable! Just like the quote!

That’s weird. What’s going on?

Let’s take a closer look at the implementation of Map, Slice, channel, etc. In my other articles, I illustrated the low-level implementation of Go Map, the low-level implementation of Go Slice, and the low-level implementation of Go Channel. We found that the low-level implementation of these types all had a pointer to another storage address, so after the make function created a specific instance of the type, In effect, multiple address Spaces are opened up in memory, and the address referenced by the pointer is “copied” as the variable is assigned, so that other variables can change the contents of the original variable.

Does that sound a little confusing? Take a look at the picture:

First, the map is instantiated and assigned

And then it’s assigned to another variable, ref

Because, for the value of the pointer variable is an address (program implementation is a string of Numbers), so, at the time of assignment, the “copy” is a string of Numbers, however, this string of Numbers the meaning behind it is another address, and the contents of the address, is precisely the map slice channel data structures such as the real underlying storage data!

So, the two variables have a “reference” effect because the same pointer variable points to memory. This “class reference” effect does not occur if there is no pointer attribute in the instantiated type data: for example:

package main

import "fmt"

func main(a){
    i := 3

    ref := i
    ref = 4

    fmt.Println(i, ref) // Printout: 3 4

    fmt.Println(fmt.Sprintf("%p %p", &i, &ref))
    // The output is 0xC000016070 0xC000016078
}
Copy the code

Take a closer look at the code above and see what it outputs. If you’re not surprised, you’ll notice that the “class reference” effect disappears

To show the “class reference” effect again, we simply create a type with a pointer attribute, which we can implement ourselves without relying on the Go base library’s Map, Slice, channel, etc

package main

import "fmt"

type Instance struct {
    Name string
    Data *int
}

func (i Instance) Store(num int) {
    *(i.Data) = num
}

func (i Instance) Show(a) int{
    return *(i.Data)
}



func main(a){
    data := 5

    i := Instance{
        Name:"hello",
        Data:&data,
    }

    ref := i
    ref.Store(7)

    fmt.Println(i.Show(), ref.Show())
    // Print out: 7 7

    fmt.Println(fmt.Sprintf("%p %p", &i, &ref))
    // Print: 0xc0000A6018 0xC0000A6030
}

Copy the code

Looking at the code above, does it implement a “class reference”? Map shows the key value, slice shows the value of a subscript. MapData [key], sliceArray[0], sliceArray[0]; The underlying implementation is all “functions” and “methods” that Go makes into syntactic sugar that we’re unaware of

Ok, now I ask you: Dare you say Go has reference types? Does it feel like: also have, also have no? 😝

For more exciting content, please follow my wechat official accountInternet Technology Nest