Reputation: 425
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.
Upvotes: 2
Views: 2432
Reputation: 42431
In Go, is a structure pointer not a reference type?
No, simply because there are no reference types in Go.
There are a few types with reference semantics, slices being the most prominent example, but even a slice is a value type and not a reference type.
Pointers in Go a plain values and really machine level addresses. When you pass around a memory address there is no "reference stuff" going on. And if you store the memory address in a variable you can take the address of that variable. Again nothing referency-like. It's values all the way down in Go.
It is basically like in C.
Upvotes: 5
Reputation: 778
The short answer is:
Pointers are passed by Value.
The function pointer
receives a copy of the memory address. It stores that copy somewhere (possibly its own stack, or something).
The memory address is just a number.
If you define a number in main
and take its address, and then you pass that number to another function and take its address, should you expect both addresses to be the same?
Upvotes: 4
Reputation: 489113
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.
Upvotes: 9