Last active
December 20, 2015 11:08
-
-
Save mortdeus/6120456 to your computer and use it in GitHub Desktop.
a few pointer pointers.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "fmt" | |
func main() { | |
a := 0 | |
b := 0 | |
// a and b's address that points to where they are located in memory. | |
fmt.Printf("main: &a = %v &b = %v\n\n", &a, &b) | |
/* | |
Note that when we pass by value, the 'a' declared in foo()'s arguments has a different | |
address than the a variable declared in main(). | |
Which means that we are actually passing in a copy of main()'s 'a' variable. | |
Any changes made to the value of foo()'s 'a' will not change the value of main()'s a. | |
*/ | |
fmt.Println("[pass by value]") | |
fmt.Println("main: before foo(a), a =", a) | |
foo(a) | |
fmt.Println("main: after foo(a), a =", a) | |
/* | |
Note that when we pass by pointer, the VALUE of bar()'s argument bptr equals | |
the address of main()'s b. Also note that the main()'s bptr address is different | |
than the addresses of main()'s b and bar()'s bptr. | |
This means that a pointer is just a integer data type that stores an address in memory | |
as it's values. When passing a pointer into a function, instead of passing in a copy of the | |
variable being pointed to, we pass in a copy of the pointer itself. The new pointer still | |
points to the same variable because the pointer's value (the variable's address) | |
doesnt change. | |
Since a pointer's value is only an integer representing the address of a variable in memory | |
we cant directly modify the value of the variable being pointed at, by modifying the | |
pointer's value. | |
For example, lets say we wanted to increment variable 'b' (b+=1). If we tried, | |
b := 0 | |
bptr := &b // bptr == 0x01000008 | |
bptr++ | |
fmt.Println(b, bptr) // b == 0, bptr == 0x01000012 | |
the value of b wont be incremented when 'bptr++' is called. What actually happens is that the | |
value stored in bptr (the address bptr is currently pointing at) will be incremented to point | |
to a new contigious block of memory. | |
[NOTE: Unlike C/C++, developers arent actually allowed to directly modify the value of a pointer | |
using arithmetic in go. Pointer arithmetic is considered an unsafe language feature, therefore | |
developers must use go's "unsafe" pkg to manipulate a pointer's value.] | |
In go and many other C-like language, accessing the variable through one of its pointers | |
requires dereferencing the pointer by prefixing the dereference operator ('*') onto | |
the pointer's identifier (e.g '*bptr'). In computer science terms this is called indirection. | |
[NOTE: In C/C++/Go/etc, The asterisk symbol ('*') has several semantical meanings depending on | |
the context in which it is used. Most developers will be familiar with it's use as the | |
multiplication operator. However, for developers unfamiliar with C-style pointer syntax, | |
the asterisk is a notoriously common source of confusion when first learning about pointers. | |
I assume the reason for this confusion stems from the fact that the asterisk is not only used to | |
dereferences a pointer, but to specify a pointer type as well. The confusion is only made much | |
worse when the address-of operator ('&') and pointer pointers (i.e. xptr_ptr => xptr => x) are | |
thrown in the mix. | |
For example, check out this function literal. | |
func(b uint32) float32 { | |
return *(*float32)(unsafe.Pointer(&b)) | |
} | |
Despite only being one line of code, it's not uncommon for even an experienced go developer | |
to require a few moments to fully digest whats going on the first time they stumble upon | |
code like this. I assume most developers, who are still new to pointers, will | |
find code like this to be intimidating and very difficult to conceptually visualize whats | |
going on semantically. | |
If you, the reader of this blog post, are struggling to understand | |
what this code does. I recommend taking it slow and using the language's spec as a codex to | |
decipher what is going on. | |
Here is code example to help point you in the right direction. | |
http://play.golang.org/p/myunEgQPRG] | |
*/ | |
bptr := &b | |
fmt.Printf("\nmain: bptr = %v (aka &b), &bptr = %v, *bptr (aka b) = %v \n\n", bptr, &bptr, *bptr) | |
fmt.Println("[pass by pointer]") | |
fmt.Println("main: before bar(bptr), b =", b) | |
bar(bptr) | |
fmt.Println("main: after bar(bptr), b =", b) | |
/* | |
To be continued.... | |
*/ | |
} | |
func foo(a int) { | |
fmt.Printf("foo: a = %v, &a = %v, \n", a, &a) | |
fmt.Println("foo: a++") | |
a++ | |
fmt.Printf("foo: a = %v, &a = %v, \n", a, &a) | |
} | |
func bar(bptr *int) { | |
fmt.Printf("bar: bptr = %v, &bptr = %v, *bptr (aka b) = %v \n", bptr, &bptr, *bptr) | |
fmt.Println("bar: *bptr++") | |
*bptr++ | |
fmt.Printf("bar: bptr = %v, &bptr = %v, *bptr (aka b) = %v \n\n", bptr, &bptr, *bptr) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment