What is a pointer?
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
a
value(!) or content of the b
variable - finally, we are getting the value of the
a
, to which the b
is pointed to (will look at the *
and &
operators bit later)расммотрим ниже)
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 the c
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 the d
variable is set - using
Printf()
‘s modifiers, the c
variable’s data type (%T
), its value (%v
), and the value from the memory address which is stored in the c
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's 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 the c
variable
The &
operator returns a variable’s memory address.
For example, let’s add to our previous example one more line and lets 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 builtin 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 hold nil
- the
b
‘s value is updated with the a
‘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 the a
‘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 the 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()
– the a
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 the a
‘s value by the reference in the b
and the c
– as a value - in the
setVal()
getting the address which is stored in the b
variable and the value which is stored there - in the
setVal()
getting the c
variable address and the value which is stored in this memory area - in the
main()
getting the a
‘s address and the value stored there - in the
main()
getting the address which is stored in the b
variable and value from there - in the
main()
getting the c
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 the setVal()
still pointed to the 0xc0000140e8
location with the 2
as its value - the
c
in the setVal()
got its new address 0xc000014130
where the 4
is stored - the
a
in the main()
now keeps the 2
value from the same 0xc0000140e8
location - the
b
in the main()
still the same as it is in the setVal()
– points to the same location and returns the same value - in the
main()
for the c
variable, nothing was changed as the c
in the setVal()
has own address 0xc000014130
, but the c
in the main()
uses the 0xc000014100
location
Actually, that’s all you need to know to better understand what pointers are and how to use them.
Similar Posts