Golang - Pointers

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

Go Playground

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
}

Go Playground

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)
}

Go Playground

*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