Shortly, the pointer is a variable which stores an address of another variable, where some data is stored.
A pointer example
Let’s take the simplest example where a pointer is used:
package main
import "fmt"
func main() {
a := 1
b := &a
fmt.Println("A: ", a)
fmt.Println("B: ", b)
fmt.Println("B: ", *b)
}
Here:
- a variable
a
is created with the integer type and value 1 - a variable
b
is created with the pointer to the integer type (see below) - and data output:
- first just the
a
value - the value(!) or content of the
b
variable - finally, we are getting the value of the
a
, to which theb
is pointed to (will look at the*
and&
operators bit later)
- first just the
Run the code:
$ go run pointers_example.go
A: 1
B: 0xc0000140e8
B: 1
On the second line, we are seeing the memory address, where the b
pointer is pointed.
On the third line – we got value from this memory address.
The pointer could be initialized in a more amply way with types specification instead of using :=
so the code will look like:
...
func main() {
var a int = 1
var b *int = &a
fmt.Println("A: ", a)
fmt.Println("B: ", b)
fmt.Println("B: ", *b)
}
...
In the var b *int = &a
line we set that the b
variable is a pointer to the integer data.
In the same way, a pointer to the string data could be created just with the *string
instead of the *int
in its data type:
...
var c *string
fmt.Printf("The C var type: %T, default value: %v\n", c, c)
var d string = "This is a string"
c = &d
fmt.Printf("The C var type: %T, default value: %v, string value: %s\n", c, c, *c)
...
here:
- the
c
variable is created as a pointer to the string data - using
Printf()
‘s modifiers thec
variable’s data type (%T
) and its value (%v
) are displayed - the
d
variable is created with the string data type and “This is a string” value - for the
c
variable the memory address of thed
variable is set - using
Printf()
‘s modifiers thec
variable’s data type (%T
), its value (%v
), and the value from the memory address which is stored in thec
Run:
$ go run pointers_example.go
The C var type: *string, default value: <nil>
The C var type: *string, default value: 0xc0000101e0, string value: This is a string
*
and & operators
We already used them in examples above but let’s take a closer look.
The *
operator is a dereference operator.
The dereference here means that we are getting a value not of a pointer (which stores an address) but from the memory address where this pointer is… Well – pointed to :-)
Let go back to our previous example:
...
fmt.Printf("The C var type: %T, default value: %v, string value: %s\n", c, c, *c)
...
Here:
-
default value: %v
with theс
– displays a value which is stored in the c variable – a memory address, where c is pointed to -
string value: %s
with the*с
– displays a value which is got after calling the memory from thec
variable
The &
operator returns a variable’s memory address.
For example, let’s add to our previous example one more line and let's display addresses using Printf()
with %p
modifier:
...
var c *string
fmt.Printf("The C var type: %T, default value: %v\n", c, c)
var d string = "This is a string"
c = &d
fmt.Printf("The C var type: %T, default value: %v, string value: %s\n", c, c, *c)
fmt.Printf("The D adress: %p\nThe C address: %p\nThe C value: %v\n", &d, &c, c)
...
Check:
$ go run pointers_example.go
The C var type: *string, default value: <nil>
The C var type: *string, default value: 0xc0000101e0, string value: This is a string
The D adress: 0xc0000101e0
The C address: 0xc00000e028
The C value: 0xc0000101e0
Here we got 0xc0000101e0 value as the d
variable address, 0xc00000e028 as the address of the c
variable, but the c
itself stores address of the d
variable – 0xc0000101e0.
Actually, a data initialization in the c
pointer variable is done by getting the address of the d
variable:
...
c = &d
...
The new()
function
To define and initialize a pointer using the var pointername *type
notation – you can use the built-in new()
Go function which accepts a data type as the first argument and will return a pointer to a memory allocated for a variable’s data:
...
a := 1
b := new(int)
fmt.Printf("A: %d, B: %v, %v\n", a, b, *b)
b = &a
fmt.Printf("A: %d, B: %v, %v\n", a, b, *b)
...
Here:
- the
a
variable is created with value 1 - the
b
variable is created which will hold a pointer to the memory returned by the new() function and which keeps 0 for now, as already allocated memory can’t holdnil
- the
b
‘s value is updated with thea
‘s address
Check:
$ go run pointers_example.go
A: 1, B: 0xc000014100, 0
A: 1, B: 0xc0000140e8, 1
Changing a pointer’s value
Well, this is not correct to say “changing a pointer’s value” as a pointer variable stores a memory address – not a value itself.
But using a pointer we can change this value in a memory location to which this pointer is pointed to.
For example:
...
a := 1
b := &a
fmt.Println("A: ", a)
fmt.Println("B: ", *b)
*b = 2
fmt.Println("B: ", *b)
...
Here:
- the
a
variable has value 1 - the
b
variable has thea
‘s address - the
a
variable value displayed - the value displayed take from the address where the
b
is pointed to - we are changing the value in this memory to the 2
- and displays the new value
Run the code:
$ go run pointers_example.go
A: 1
B: 1
B: 2
Passing a pointer as a function’s argument
You can pass a pointer to a function as its argument.
For example:
package main
import "fmt"
func setVal(b *int) {
*b = 2
}
func main() {
a := 1
b := &a
fmt.Println("Init values")
fmt.Println("A: ", a)
fmt.Println("B: ", *b)
setVal(b)
fmt.Println("Changed values")
fmt.Println("A: ", a)
fmt.Println("B: ", *b)
}
Here we are creating a setVal()
function which accepts an integer pointer as an argument and will change a value in the address passed with this pointer.
Check it:
$ go run pointers_example.go
Init values
A: 1
B: 1
Changed values
A: 2
B: 2
After calling the setVal()
– thea
and b
will display a new value.
Even more – we could pass just an address of the a
‘s variable to the setVal()
:
...
func setVal(b *int) {
*b = 2
}
func main() {
a := 1
fmt.Println("Init values")
fmt.Println("A: ", a)
setVal(&a)
fmt.Println("Changed values")
fmt.Println("A: ", a)
}
The result is:
$ go run pointers_example.go
Init values
A: 1
Changed values
A: 2
Functions: passing arguments by value and by reference
A bit offtopic here but using the example above the difference between passing argument by value and argument by reference also can be displayed.
Let’s update this example:
...
func setVal(b *int, c int) {
*b = 2
c = 4
fmt.Printf("B from setVal(). Poiner to: %p, val: %v\n", b, *b)
fmt.Printf("C from setVal(). Addr: %p, val: %v\n", &c, c)
}
func main() {
a := 1
b := &a
c := 3
fmt.Println("Init values")
fmt.Printf("A from main(). Addr: %p, val: %v\n", &a, a)
fmt.Printf("B from main(). Poiner to: %p, val: %v\n", b, *b)
fmt.Printf("C from main(). Addr: %p, val: %v\n", &c, c)
fmt.Println("Changed values")
setVal(b, c)
fmt.Printf("A from main(). Addr: %p, val: %v\n", &a, a)
fmt.Printf("B from main(). Poiner to: %p, val: %v\n", b, *b)
fmt.Printf("C from main(). Addr: %p, val: %v\n", &c, c)
}
Here:
- get the
a
variable address and its value - get the address which is stored in the
b
variable and then the data which is stored on this address - get the
c
variable address and its value - call the
setVal()
function and pass thea
‘s value by the reference in theb
and thec
– as a value - in the
setVal()
getting the address which is stored in theb
variable and the value which is stored there - in the
setVal()
getting thec
variable address and the value which is stored in this memory area - in the
main()
getting thea
‘s address and the value stored there - in the
main()
getting the address which is stored in theb
variable and value from there - in the
main()
getting thec
variable address and the value which is stored there
Run it:
$ go run pointers_example.go
Init values
A from main(). Addr: 0xc0000140e8, val: 1
B from main(). Poiner to: 0xc0000140e8, val: 1
C from main(). Addr: 0xc000014100, val: 3
Changed values
B from setVal(). Poiner to: 0xc0000140e8, val: 2
C from setVal(). Addr: 0xc000014130, val: 4
A from main(). Addr: 0xc0000140e8, val: 2
B from main(). Poiner to: 0xc0000140e8, val: 2
C from main(). Addr: 0xc000014100, val: 3
Here:
- the
a
is stored in the 0xc0000140e8 location and has value 1 - the
b
is pointed to the same location 0xc0000140e8 and returns the same 1 value - the
c
is stored in the 0xc000014100 location with the 3 value - the
setVal()
is called - the
b
in thesetVal()
still pointed to the 0xc0000140e8 location with the 2 as its value - the
c
in thesetVal()
got its new address 0xc000014130 where the 4 is stored - the
a
in themain()
now keeps the 2 value from the same 0xc0000140e8 location -
the b
in themain()
still the same as it is in thesetVal()
– points to the same location and returns the same value - in the
main()
for thec
variable nothing was changed as thec
in thesetVal()
has own address 0xc000014130, but thec
in themain()
uses the 0xc000014100 location
Actually, that’s all need to know to better understand what pointers are and how to use them.
Top comments (2)
Great article, Arseny!
Very useful for those who are learning Golang 👍
Thanks, Nick :-)
Will add a few more soon.