Pointers

By Syncfusion®, Inc. · Last updated

Go pointers: memory addresses, the & and * operators, changing values through pointers, nil pointers, pass by value vs pointer parameters, and struct pointers.

What Are Pointers?

Pointer = a variable that stores the memory address of another variable.

  • A normal variable holds a value, like 70.
  • A pointer holds where a value lives, like 0xc000012080.
  • Through the pointer, you can read or change that value.

Simple lesson: a pointer is an arrow that points to another variable's box.

A Real-World Example

Think of a student's locker at school.

  • The locker holds books.
  • The locker number, like "B-12", tells you where it is.
  • If you give a friend your locker number, they can go and put a book inside.
  • You did not give them the books. You gave them the address.

Now the mapping:

  • The locker = a variable.
  • The books inside = its value.
  • The locker number = its memory address.
  • A note with the locker number = a pointer.

Go has pointers, but it keeps them safe:

  • no pointer arithmetic (you cannot do p + 1)
  • memory is freed automatically by the garbage collector
  • most of the time, you use pointers to let a function change a value, or to avoid copying big structs

What You Will Learn

In this lesson, you will learn:

  1. What pointers and memory addresses are, and why pointers are used.
  2. The address operator & and the dereference operator *.
  3. How to read and change values through a pointer, and what a nil pointer is.
  4. How pass by value works, and how pointer parameters change original values.
  5. How pointers work with structs.

Pointer Fundamentals

Memory Addresses

Memory = the computer's storage for running programs, like a long row of numbered lockers.

Memory address = the number of one locker. It is usually printed in hexadecimal, like 0xc000012080.

Print an address

package main

import "fmt"

func main() {
	marks := 70
	fmt.Println(marks)
	fmt.Println(&marks)
}
  • &marks gives the address of marks.
  • The address is different on each run and each computer.

Pointer Variables

Pointer type = *T, meaning "a pointer to a T".

  • *int = a pointer to an int.
  • *string = a pointer to a string.
  • The zero value of every pointer is nil.

Why Pointers Are Used

  • Let a function change a variable that belongs to the caller.
  • Avoid copying a big value, like a struct with many fields.
  • Share one value between many parts of a program.
  • Say "no value" with nil, for something that is optional.

Simple lesson: a pointer of type *T stores the address of a T.

Pointer Operators

Step through the picture. Watch the arrow and the box.

Address Operator (&)

& = "the address of". &x gives a pointer to x.

Dereference Operator (*)

Dereference = follow the pointer to the value it points to.

* = "the value at". *p gives the value p points to.

The two pointer operators

& and *

package main

import "fmt"

func main() {
	marks := 70
	p := &marks
	fmt.Println(*p)
	*p = 85
	fmt.Println(marks)
	fmt.Println(p == &marks)
}
70
85
true
  • p := &marks makes p point to marks.
  • *p = 85 changes marks, through the pointer.
  • p == &marks is true: both hold the same address.

Simple lesson: & makes a pointer, and * follows it to the value.

Working with Pointers

Dereferencing Pointers

Two pointers can point to the same box.

Two pointers, one box

package main

import "fmt"

func main() {
	score := 10
	a := &score
	b := &score
	*a += 5
	*b += 5
	fmt.Println(score, *a, *b)
}
20 20 20
  • Both a and b point to score.
  • Changing through either one changes the same box.

Modifying Values Through Pointers

You have seen *p = 85. Go also has new, which makes a fresh box that you reach only through a pointer.

new(T) = make a new zero-value T and give back a pointer to it.

new()

package main

import "fmt"

func main() {
	count := new(int)
	fmt.Println(*count)
	*count = 3
	*count++
	fmt.Println(*count)
}
0
4
  • new(int) makes a box holding 0, and returns *int.
  • This box has no name. You reach it only through count.

Nil Pointers

Nil pointer = a pointer that points to nothing.

Check for nil first

package main

import "fmt"

func main() {
	var p *int
	fmt.Println(p == nil)
	if p != nil {
		fmt.Println(*p)
	} else {
		fmt.Println("p points to nothing")
	}
}
true
p points to nothing

Following a nil pointer

package main

import "fmt"

func main() {
	var p *int
	fmt.Println(*p)
}
  • Following a nil pointer stops the program.
  • The message is panic: runtime error: invalid memory address or nil pointer dereference.
  • Always check p != nil when a pointer might be empty.

A pointer you declare with var p *int is nil until you point it somewhere.

Give it an address first: p = &x or p = new(int).

No Pointer Arithmetic

  • In C, you can do p + 1 to move to the next box.
  • Go does not allow this. It prevents a whole class of memory bugs.

Simple lesson: change values with *p = ..., make pointers with & or new, and check for nil before following one.

Pointers in Functions

Pass by Value

Pass by value = a function gets a copy of each argument. See Functions.

Copy vs pointer

Pointer Parameters

Pointer parameter = a parameter of type *T. The caller passes an address with &.

Swap two values

package main

import "fmt"

func swap(a, b *string) {
	*a, *b = *b, *a
}

func main() {
	first, second := "Asha", "Ravi"
	swap(&first, &second)
	fmt.Println(first, second)
}
Ravi Asha

Updating Original Values

  • Use a pointer parameter when the function's job is to change the caller's value.
  • Otherwise, prefer returning a new value. It is easier to read.
  • Slices and maps already share their data, so you rarely need a pointer to them.

Easy way to remember

  • Function needs to change your variable → pass &x, take *T.
  • Function only reads it → pass x.

Simple lesson: Go passes copies, so pass a pointer when a function must change the original.

Pointers and Structs

Struct = a group of named fields, like a student record. You will learn structs fully in Structs and Methods.

Struct Pointers

A pointer to a struct

package main

import "fmt"

type Student struct {
	Name  string
	Marks int
}

func main() {
	s := Student{Name: "Meena", Marks: 80}
	p := &s
	p.Marks = 88
	fmt.Println(s.Marks)

	q := &Student{Name: "Kabir", Marks: 75}
	fmt.Println(q.Name, q.Marks)
}
88
Kabir 75
  • &Student{...} makes a struct and gives a pointer to it in one step.

Accessing Struct Fields Through Pointers

  • With a pointer p to a struct, write p.Marks.
  • Go reads it as (*p).Marks for you.
  • This automatic step is why struct pointers are easy to use.

A function that updates a struct

package main

import "fmt"

type Student struct {
	Name  string
	Marks int
}

func addGrace(s *Student, n int) {
	s.Marks += n
}

func main() {
	asha := Student{"Asha", 32}
	addGrace(&asha, 3)
	fmt.Println(asha)
}
{Asha 35}
  • Without the pointer, addGrace would change only a copy of asha.

Simple lesson: use &Struct{...} and p.Field to work with struct pointers. Go follows the pointer for you.

TL;DR

  1. Pointer = holds the address of a value. Type *T. Zero value nil.
  2. &x = the address of x. *p = the value at p.
  3. *p = v = change the value through the pointer.
  4. new(T) = a new zero T, returned as *T.
  5. Nil pointer = points to nothing. Following it panics. Check p != nil.
  6. Pass by value = functions get copies. Pass &x to let them change x.
  7. Struct pointer = p := &Student{...}, then p.Marks (Go adds the *).
  8. No pointer arithmetic in Go.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
var p *int then *p = 5A pointer comes with a boxA new pointer is nilA nil pointer panic. Use new(int) or &x.
addBonus(marks) with a *int parameterGo converts itPass the address: &markscannot use marks (variable of type int) as *int
p = 85 to change the value= goes through the pointerUse *p = 85A type error.
Changing a parameter to change the callerParameters are sharedThey are copiesThe caller's value does not change.
p + 1 to move to the next valueGo has pointer arithmeticIt does notA build error.
Pointers to slices and maps everywhereThey are always neededSlices and maps already share dataHarder code for no gain.
Not checking nilPointers are always setThey may be nilA crash at runtime.

Check Yourself

Exercises

What to Remember

  • A pointer stores an address. Its type is *T, and its zero value is nil.
  • &x gives a pointer. *p follows it.
  • Functions get copies. Pass &x when a function must change x.
  • Check p != nil before following a pointer that might be empty.
  • With a struct pointer, p.Field works. Go adds the * for you.

Next: Structs and Methods. You will learn how to group related values into your own types, and give them behaviour.