I don't understand the behavior of Go variables. I want you to tell me. See the example implementation below.
package main
import (
"fmt"
)
func pointer(ip *Num) {
fmt.Printf("pointer type [%T] : %p\n", &ip, &ip)
}
func pointerpointer(ip **Num) {
fmt.Printf("pointerpointer type [%T] : %p\n", ip, ip)
}
func main() {
pnum := &Num{i: 3}
fmt.Printf("main type [%T] : %p\n", &pnum, &pnum)
pointer(pnum)
pointerpointer(&pnum)
}
type Num struct {
i int
}
https://play.golang.org/p/LxDAgopxeh0
main type [**main.Num] : 0x40c138
pointer type [**main.Num] : 0x40c148
pointerpointer type [**main.Num] : 0x40c138
I store the struct Num pointer as a variable [pnum]. The address that can be acquired when this is passed to the pointer function is different from the address that can be acquired in the main function. Why??
It has been confirmed that the same address as the main function can be obtained by referring to the pointer pointer as the pointerpointer function.
pnum, in main, is an actual variable: a box, floating in memory, containing a pointer of type *Num.
0x40c138
+--------+
| *---|--->
+--------+
To what does pnum point? Well, Num{i: 3} created an unnamed variable, floating in memory somewhere. We haven't printed it. I modified your Playground example to add one more fmt.Printf call to find out:
main type [**main.Num] : 0x40c138
pnum points to [*main.Num] : 0x40e020
or:
0x40c138 0x40e020
+--------+ +--------+
| *---|---> | i: 3 |
+--------+ +--------+
Now let's move on: main calls pointer, passing to it the value stored in pnum—the 0x40c138 value which lets the computer find the box holding the unnamed instance holding i=3. This value is stored in memory somewhere. Where is that somewhere? Why, it's the number you printed, because you print &ip. Fortunately the Playground is very deterministic, so it's also the one I printed:
pointer type [**main.Num] : 0x40c148
So we can add another box to our drawing:
0x40c138 0x40e020
+--------+ +--------+
|0x40e020|---> | i: 3 |
+--------+ +--------+
^
0x40c148 |
+--------+ |
|0x40e020|---------+
+--------+
Now pointer returns, discarding the box at 0x40c148 that holds 0x40e020, and you call pointerpointer, passing &pnum as a value. Function pointerpointer now runs with a box at some location. Where is that location? We don't know; we never print it. We do print what's in the box at that location, and what's in the box is 0x40c138:
0x40c138 0x40e020
+--------+ +--------+
|0x40e020|---> | i: 3 |
+--------+ +--------+
^
|
????????
+--------+
|0x40c138|
+--------+
We can add one more fmt.Printf to the program to find where this last box actually is in memory and run again:
pointerpointer's &ip is [***main.Num] : 0x40c150
so replace the eight question marks with 0x40c150.
The overall rule here is simple enough: Every variable has some existence somewhere, unless the compiler can optimize it away. Taking the address of the variable, with &x, tends to prevent the compiler from optimizing the variable itself away. The type of &x is pointer to ___ where the blank is the type of x. The value of &x is the address of the variable.
In this case, you
Num instance,pnum to that value(all in the first line of main). Then you took the address of pnum itself, which means pnum also has to live somewhere in memory. Those are the two boxes we drew initially. You printed that address's type and value on your second line.
You then passed the value stored in pnum to func pointer, which stored the value in a variable. So that created another, separate variable, which has its own address.
Without worrying about what function pointer did, you then passed the address of pnum to func pointerpointer. This address is a value, and pointerpointer stored that value in a variable. This, too, created another separate variable.
Throughout all of this, the address of the anonymous Num with i=3 never moved. Neither did the address of pnum itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With