Pointers
Pointers let a function reach back and change the caller's data instead of working on a copy. Go's pointers are far gentler than C's — no pointer arithmetic, garbage-collected memory — but the two operators & (address-of) and * (dereference) unlock efficient, mutable code. This lesson demystifies them once and for all.
Learn Pointers in our free Go course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick recall.
Part of the free Go course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ Address-of & Dereference
Two operators do all the work. &x gives you the address of x — a value of type *int if x is an int . *p dereferences the pointer: it follows the address to read or write the value living there. Writing through a pointer changes the original variable — that's the whole point.
2️⃣ Pointers as Function Parameters
This is where pointers earn their keep. Go passes everything by value — a function receives a copy, so editing a plain parameter never touches the caller's variable. Pass a pointer instead and the function can modify the original by dereferencing it. Compare the two functions below: only the pointer version's change survives.
3️⃣ new, nil & Structs
The built-in new(T) allocates a zeroed T and returns a *T . Every pointer's zero value is nil , and dereferencing a nil pointer panics — so guard with a check when a pointer might be unset. A lovely convenience: with a pointer to a struct, Go lets you write p.Field and auto-dereferences for you — no need for the clunky (*p).Field .
Your turn — take an address, then write through the pointer.
Now pass an address so a function can reset your variable.
These lines change a variable through a pointer, but they're scrambled. Put them in order so the program prints 42 (assume a func main wraps them).
Why: x must exist before you can take its address. p := &x captures that address; *p = 42 writes through the pointer into x . Printing x last shows 42 because the write changed the original variable.
Predict the output before you reveal the answer.
8 — p points at x , so writing *p = 8 changes the original x to 8.
{'func f(n int) ; x := 4; f(x); fmt.Println(x)'}
4 — f takes n by value, so it edits a copy. The original x is unchanged. (Pass *int to mutate it.)
true — the zero value of any pointer type is nil . Dereferencing it would panic, so always check first.
Two main reasons: to let a function mutate the caller's value, and to avoid copying a large struct on every call. For small, read-only values, a plain value is clearer.
Much safer. There's no pointer arithmetic, memory is garbage-collected, and returning the address of a local variable is perfectly fine — Go keeps it alive for you.
Q: Why can I write p.Field instead of (*p).Field ?
Go automatically dereferences a struct pointer when you access a field or call a method, purely as a convenience. Both forms mean the same thing.
Write a deposit(a *Account, amount int) that adds to a.Balance , then call it with &acc . Because you pass the address, the change is visible in main . Write it yourself and match the example output.
Practice quiz
What does the & operator do in Go?
- Dereferences a pointer
- Takes the address of a variable
- Multiplies values
- Declares a pointer type
Answer: Takes the address of a variable. &x yields a pointer holding the memory address of x.
What does the * operator do when applied to a pointer value?
- Takes its address
- Dereferences it to read or write the pointed-to value
- Frees memory
- Compares pointers
Answer: Dereferences it to read or write the pointed-to value. *p dereferences the pointer to access the value it points to.
What is the zero value of a pointer?
- 0
- nil
- An empty struct
- Undefined
Answer: nil. An uninitialized pointer is nil.
What happens if you dereference a nil pointer?
- Returns 0
- Returns nil
- Runtime panic
- Compile error
Answer: Runtime panic. Dereferencing a nil pointer causes a runtime panic (invalid memory address).
After p := &x; *p = 20, what is the value of x?
- 1
- 20
- 0
- nil
Answer: 20. Writing through the pointer with *p = 20 changes the original variable x to 20.
How can a function modify the caller's variable?
- Pass it by value
- Take a *T parameter and call with &x
- Return it only
- Use a global
Answer: Take a *T parameter and call with &x. Pass a pointer (*T) and call with the address (&x) so the function mutates the original.
What does new(T) return?
- A value of type T
- A pointer to a zeroed T
- nil
- An error
Answer: A pointer to a zeroed T. new(T) allocates a zeroed T and returns a *T pointing to it.
Does Go support pointer arithmetic like C (p++)?
- Yes
- No
- Only for slices
- Only with unsafe
Answer: No. Ordinary Go has no pointer arithmetic — pointers can't be incremented.
Given p *Account, how do you read a struct field through it?
- (*p).Field only
- p.Field — Go auto-dereferences
- p->Field
- Field(p)
Answer: p.Field — Go auto-dereferences. Go auto-dereferences struct pointers, so p.Field works directly.
Passing a large struct by pointer instead of by value mainly avoids:
- Type errors
- Copying the whole struct
- Imports
- Panics
Answer: Copying the whole struct. A pointer passes an address, avoiding a full copy of the struct's data.