Golang - Pointers
Table of Contents
A pointer is a variable that’s holding the memory location or address of an object or value. To declare a pointer in Go, you have to specify the type of data that the memory address will hold.
Each variable in a program has a memory address. The memory address is in hexadecimal format, starting with 0x
.
When you’re declaring a variable, like test := "testing"
, you’re basically telling the Go Runtime
to store the string "testing"
in a memory address for later use. Maybe you’ll pass the variable in a function like fmt.Println(test)
.
Instead of calling fmt.Println(0xF205...)
, you’ll use fmt.Println(test)
. It’s impossible to remember all the memory addresses in your program. That’s why we use variables. Think of it like the DNS. We remember Domain Names, not IP addresses, right.
Every variable represents a memory address pointing to a value like so:
0xc0000b6018 = 101
A variable pointing to a memory address of another variable(or data in memory) is called a pointer.
Note: A pointer is a memory location with a type.
Declaring a pointer #
The code below, declares a nil pointer. A nil pointer is basically a pointer that’s currently empty. In this case, it’s empty but it will only points or holds a memory address that’s holding data of type int
:
package main
import (
"fmt"
)
func main() {
var pointer *int
fmt.Println(pointer)
fmt.Printf("pointer: %v\t pointer: %p\n", pointer, pointer)
}
The **pointer**
variable can only hold a memory address that’s holding data of type int
.
More examples:
var pBool *bool
var pString *string
var pMap *map[string]string
var pChan *chan int
fmt.Printf("The value of pBool is %v, and the address is %p.", pBool, pBool)
...
...
---SNIP
Those are nil pointers. The value is nil
and the address is 0x0
. It is Golang’s way of saying there’s nothing there.
The value of pBool is <nil>, and the address is 0x0.
Declare a pointer with new() #
new()
is a little bit different. The syntax is new(Type) *Type
.
It takes a type
as argument, and returns a pointer(not 0x0) of that type with allocated memory(like 0xc00002c008).
package main
import (
"fmt"
)
func main() {
pointer := new(int)
fmt.Printf("value: %v\t pointer: %p\n", pointer, pointer)
}
The line pointer := new(int)
says: I need a piece of memory that’s big enough to store an int
. Find it for me and give me the address.
value: 0xc00002c008 pointer: 0xc00002c008
& and * Operator #
An &
operator is used to retrieve the memory address of a variable, Or initialize a pointer variable.
var x = 101
var ptr = &x
The &x
retrieves the memory address of x and stores it in ptr
. A complete example:
package main
import "fmt"
var p = fmt.Println
func main() {
var x = 101
var ptr = &x
p("The memory address of x:", &x)
p("The memory address of ptr:", &ptr)
p("The value of ptr:", ptr)
}
// output
The memory address of x: 0xc0000b4020
The memory address of ptr: 0xc0000b6018
The value of ptr: 0xc0000b4020
Dereferencing a Pointer #
Dereferencing is used to access or manipulate data contained in a memory location pointed to by a pointer variable or simply pointer. Or get the value that a pointer(memory address) points to.
To access or manipulate the data of a pointer or to dereference a pointer, an *
operator is used in front of the pointer variable:
package main
import "fmt"
var p = fmt.Println
func main() {
var x = "Having fun with pointers"
var ptr = &x
p(*ptr) // print the value of the pointer
*ptr = "Still having fun with pointers" // dereference the pointer with new data
p(x) // prints new data
}
A Pointer as an Argument in a Function #
To define a function() that takes a memory address as an argument or simply a pointer, *T
is used as the type. In this case, *int
which stands for a pointer to an int, or a memory address whose value is an int.
You give the function() a memory address by prepending the variable with an &
operator. Inside the function, an *
is used to access the value, hence dereferencing a pointer.
package main
import (
"fmt"
)
func zero(ptr *int){
*ptr = 0 // store 0 in the memory address *ptr is refering to
}
func main() {
x := 5
zero(&x) // the & operator finds the memory address of variable x
fmt.Println(x)
}
*int
means: I need a memory address that points to data of type int.
Behind the scene, ptr
will point to a memory address that looks like this (in hexadecimal): ptr = 0xc00002c008
On the next line, the asterisk *
is used again(now in front of the pointer variable) to dereference the pointer, hence access/manipulate the value stored in that particular memory address. Behind the scene => replace the current value of 0xc00002c008
or ptr
with 0.
If I wrote: ptr = 0
instead *ptr = 0
, the Go Compiler will return an error saying: “*cannot use 0 (type int) as type int in assignment”.
Because ptr
is like: var ptr *int
or ptr = new(int)
. It will only accept or store a memory address. You can’t just assign a value directly. You have to dereference it first.
“Blessed are those who are persecuted because of righteousness, for theirs is the kingdom of heaven.”
Jesus Christ, Matthew 5:10