Pointers

3-minute read
Table of Contents

We can work with variables using their identifiers but we can also work with the underlying memory in which we store those variables. Pointers allow us to do this. A pointer is a variable that stores the memory address of another variable.

Memory locations

We can get the memory location of a variable by using the ampersand & before the name of the variable:

package main

import "fmt"

func main() {
	myName := "John"
	fmt.Println("my name:", myName, ", memory address:", &myName)
}
print-memory-location.go
Copy

Dereferencing a pointer

This is where we get the actual value being stored in the memory address that the pointer is referring to. We use the unary operator * to get the value stored at the location:

package main

import "fmt"

func main() {
	myInt := 3
	fmt.Println("my integer:", myInt)
	var myPtr *int
	myPtr = &myInt
	fmt.Println("my pointer:", myPtr)
	fmt.Println("the integer stored at my pointer:", *myPtr)

	myInt = 5
	fmt.Println("my pointer:", myPtr)
	fmt.Println("the integer stored at my pointer:", *myPtr)
}
dereference-pointer.go
Copy

When we change the value of the variable myInt to $5$, the value attained by dereferencing the pointer becomes $5$ as well.

The pointer notation in Golang is much more intuitive than that in languages like C. For example, reference *string declares a pointer variable called reference that points to a string.

Pass by value

Here we pass the value of a variable as a copy in memory into a function. The function then returns a result which requires more memory.

package main

import (
	"fmt"
	"strings"
)

func replaceSpace(str string) string {
	return strings.ReplaceAll(str, " ", "_")
}

func main() {
	name := "John Doe"
	fmt.Println("my string:", name)
	newName := replaceSpace(name)
	fmt.Println("my new string:", newName)
}
pass-by-value.go
Copy

Here we use memory for name, newName and the local automatic variable str.

Pass by reference

This is where we pass a pointer to the variable into a function. This enables us to avoid having to create a copy of the variable locally within the function, instead editing the memory address of the variable directly. This is especially important in systems where memory is limited and we can’t spare the extra memory to make copies.

package main

import (
	"fmt"
	"strings"
)

func replaceSpaceUsingPtr(str *string) {
	*str = strings.ReplaceAll(*str, " ", "_")
}

func main() {
	name := "John Doe"
	fmt.Println("my string:", name)
	replaceSpaceUsingPtr(&name)
	fmt.Println("my new string:", name)
}
pass-by-reference.go
Copy

Here we only have memory for the variable name and the local automatic variable str.

The value of pass by reference is especially evident when we are working with larger data types. Imagine if we had to use a string that was 1 million characters long - to create a copy of each byte in the string would be more taxing than using a pointer to the same string we wish to work on.

Support us via BuyMeACoffee