Reputation: 149
My question is pretty simple, but I believe it hides important features of Go variables initialization.
If we have two variables
i := 5
d := &i
And we want to print them
fmt.Printf("The address of i value is %d\n", &i)
fmt.Printf("The value of d is %d\n", d)
fmt.Printf("The address of d is %d", &d)
The output will be something like
The address of i value is 824633835664
The value of d is 824633835664
The address of d is 824633778224
So it looks like &i
returns the address of its value 5
. It's understood.
What is not understood is that &d
returns us what? The address of variable i
?
So is it so implemented that the value has its own address and the variable (which is an alias for the value memory address) also has its own address in memory?
Otherwise &d
would return us the address of 5
Upvotes: 1
Views: 2522
Reputation: 487825
Go is pretty conventional here. You can imagine the system's overall memory to be a big box that holds smaller boxes. Each of the smaller boxes can hold something as well.
Declaring a variable:
var i int
tells the Go system that it should allocate a small box somewhere—you don't have control over where as that's up to the Go system—that is big enough to hold some int
value. Since we have not yet put any value into the box, Go pre-fills it with zero:
i:
+-----+
| 0 |
+-----+
If we set i
to 5
, that puts 5 inside the box. Using i := 5
is a shorthand way to tell the Go compiler: create the variable i
somewhere and put 5 in the box all in one line, so that we get:
i:
+-----+
| 5 |
+-----+
So you have this setup now, and because you combined the declaration and assignment, it's easy for the compiler to avoid any wasted effort: it won't have to first set i
to zero, then set i
to 5. (The compiler can be smart and avoid the wasted effort even if you didn't use the short-declaration form, of course, but it's nicer for us humans to not have to think about that: the short-declaration form lets us just think of this as create i
with it set to 5 already.)
We also have:
var d *int
which tells the Go system to allocate a box big enough to hold *int
values. That might be the same size as int
values, or bigger, or smaller. I'll draw it about twice as big, which may or may not be the case on your system:
d:
+-----------+
| nil |
+-----------+
If we have not yet set d
, Go fills it in with the appropriate zero value, i.e., nil
.
If we now set d
to point to i
, this changes d
so that it points to i
:
d: i:
+-----------+ +-----+
| *-----------> | 5 |
+-----------+ +-----+
Here, you used d := &i
to declare d
and then also set d
to &i
, so that we have this situation now (without any effort wasted on setting d
to nil first, then replacing the nil
with the value for &i
, too).
Your first two calls to fmt.Printf
pass, first, the value &i
, and then the value stored in d
, which is also &i
, so these two values print out the same. (Note that %d
is not usually a good way to print a pointer value, though the Go package specification for fmt
says that it will work. Usually pointers are best printed in hexadecimal, for modern computers; the %p
format does so.)
Your last call passes the value &d
. We haven't yet drawn a memory-box for that, but to pass the value, Go has to stuff it into some box somewhere.1 We can draw that now:
unnamed:
+-----------+
| * |
+-----|-----+
|
d: v i:
+-----------+ +-----+
| *-----------> | 5 |
+-----------+ +-----+
So the value &d
is somewhere in a box in memory, in a location that doesn't have a name (d
and i
are in locations that do have names we can use to talk about them). Inside that box is a pointer value. The pointer value points to the box that holds d
.
The last call to fmt.Printf
sends this pointer value—this &d
value, as stored in the unnamed box—to fmt.Printf
so that fmt.Printf
can print it. This value must of course be different from the value stored inside the d
box, because d
points to i
. So the final printed value is different.
This program overall has a set of possible Right Answers. We don't know what the three values that are printed will be, but we do know that the first and second value will be the same and the third printed value will not match the first two. If this doesn't happen, the system you're using has not implemented the Go language correctly.2
1If there's a fast way to pass values that doesn't involve using memory boxes, Go is allowed to do that, as long as you can't tell, within the normal language rules, that Go is doing that. But the same applies to ordinary variables!
The Go compiler and runtime system only has to produce the right answer, regardless of how much compile-time magic it applies to make the right answer come out. Moreover, "right answers" are only defined for some subset of possible programs.
2This might be a slight overstatement. I'm not sure whether Go forbids a compacting garbage collector that moves objects around in memory, updating all the pointers. Actual existing Go systems do not have compacting collectors, but the way one can tell this is by using code like this, that's at least a little bit suspect. If Go does allow compacting collectors, the printed values for pointers might change, so that the first two values printed could differ.
Upvotes: 6