I am just playing around with Go and do not yet have a good mental model of when structs are passed by value or by reference.
This may be a very dumb question but I just want to experiment a bit and see if I am still working on the same object or I have made a copy of it (passed it by value).
Is there a way to print the pointer (or internal id if pointer value is changed by gc) of an object?
package main import ( "runtime" ) type Something struct { number int queue chan int } func gotest( s *Something, done chan bool ) { println( "from gotest:") println( &s ) for num := range s.queue { println( num ) s.number = num } done <- true } func main() { runtime.GOMAXPROCS(4) s := new(Something) println(&s) s.queue = make(chan int) done := make(chan bool) go gotest(s, done) s.queue <- 42 close(s.queue) <- done println(&s) println(s.number) }
gives on my windows (8g compiled version):
0x4930d4 from gotest: 0x4974d8 42 0x4930d4 42
Why does the pointer value from within the go routine show a different value? The quantity on the original object did get changed so it was working with the same object. Is there a way to see an object id that is persistent?
In Go, to print the memory address of a variable, struct, array, slice, map, or any other structure, you need to generate a pointer to the value with the address operator & and use the fmt. Println() function (or any other print function from the fmt package) to write the value address to the standard output.
In Go a pointer is represented using the * (asterisk) character followed by the type of the stored value. In the zero function xPtr is a pointer to an int . * is also used to “dereference” pointer variables. Dereferencing a pointer gives us access to the value the pointer points to.
To get the value pointed to by a pointer, you need to use the dereferencing operator * (e.g., if pNumber is a int pointer, *pNumber returns the value pointed to by pNumber .
Go has pointers. A pointer holds the memory address of a value. The data is stored in the memory when the program runs and each has a number- the memory address, which can be assigned to a pointer, and through which we can find the data stored in memory.
Go function arguments are passed by value.
First, let's discard the irrelevant parts of your example, so that we can easily see that you are merely passing an argument by value. For example,
package main import "fmt" func byval(q *int) { fmt.Printf("3. byval -- q %T: &q=%p q=&i=%p *q=i=%v\n", q, &q, q, *q) *q = 4143 fmt.Printf("4. byval -- q %T: &q=%p q=&i=%p *q=i=%v\n", q, &q, q, *q) q = nil } func main() { i := int(42) fmt.Printf("1. main -- i %T: &i=%p i=%v\n", i, &i, i) p := &i fmt.Printf("2. main -- p %T: &p=%p p=&i=%p *p=i=%v\n", p, &p, p, *p) byval(p) fmt.Printf("5. main -- p %T: &p=%p p=&i=%p *p=i=%v\n", p, &p, p, *p) fmt.Printf("6. main -- i %T: &i=%p i=%v\n", i, &i, i) }
Output:
1. main -- i int: &i=0xf840000040 i=42 2. main -- p *int: &p=0xf8400000f0 p=&i=0xf840000040 *p=i=42 3. byval -- q *int: &q=0xf8400000d8 q=&i=0xf840000040 *q=i=42 4. byval -- q *int: &q=0xf8400000d8 q=&i=0xf840000040 *q=i=4143 5. main -- p *int: &p=0xf8400000f0 p=&i=0xf840000040 *p=i=4143 6. main -- i int: &i=0xf840000040 i=4143
In function main
, i
is an int
variable at memory location (&i
) 0xf800000040
with an initial value (i
) 42
.
In function main
, p
is a pointer to an int
variable at memory location (&p
) 0xf8000000f0
with a value (p
=&i
) 0xf800000040
which points to an int
value (*p
=i
) 42
.
In function main
, byval(p)
is a function call which assigns the value (p
=&i
) 0xf800000040
of the argument at memory location (&p
) 0xf8000000f0
to the function byval
parameter q
at memory location (&q
) 0xf8000000d8
. In other words, memory is allocated for the byval
parameter q
and the value of the main
byval
argument p
is assigned to it; the values of p
and q
are initially the same, but the variables p
and q
are distinct.
In function byval
, using pointer q
(*int
), which is a copy of pointer p
(*int
), integer *q
(i
) is set to a new int value 4143
. At the end before returning. the pointer q
is set to nil
(zero value), which has no effect on p
since q
is a copy.
In function main
, p
is a pointer to an int
variable at memory location (&p
) 0xf8000000f0
with a value (p
=&i
) 0xf800000040
which points to a new int
value (*p
=i
) 4143
.
In function main
, i
is an int
variable at memory location (&i
) 0xf800000040
with a final value (i
) 4143
.
In your example, the function main
variable s
used as an argument to the function gotest
call is not the same as the function gotest
parameter s
. They have the same name, but are different variables with different scopes and memory locations. The function parameter s
hides the function call argument s
. That's why in my example, I named the argument and parameter variables p
and q
respectively to emphasize the difference.
In your example, (&s
) 0x4930d4
is the address of the memory location for the variable s
in function main
that is used as an argument to the function call gotest(s, done)
, and 0x4974d8
is the address of the memory location for the function gotest
parameter s
. If you set parameter s = nil
at the end of function gotest
, it has no effect on variable s
in main
; s
in main
and s
in gotest
are distinct memory locations. In terms of types, &s
is **Something
, s
is *Something
, and *s
is Something
. &s
is a pointer to (address of memory location) s
, which is a pointer to (address of memory location) an anonymous variable of type Something
. In terms of values, main.&s != gotest.&s
, main.s == gotest.s
, main.*s == gotest.*s
, and main.s.number == gotest.s.number
.
You should take mkb's sage advice and stop using println(&s)
. Use the fmt
package, for example,
fmt.Printf("%v %p %v\n", &s, s, *s)
Pointers have the same value when they point to the same memory location; pointers have different values when they point to different memory locations.
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