Learn Go← Dashboard

Address-of (&), dereference (*), and pass semantics.

Pointer Basics

A pointer is a value that holds the memory address of another value. Go pointers are simpler than C's: you can't do pointer arithmetic, can't cast to void*, can't accidentally read past the end. They're a tool for sharing and mutating without copying.

& and *

  • &x — take the address of x (produces a *T).
  • *pdereference the pointer (gives you back the T).
go playground
Loading...

Pointers in functions

By default Go passes arguments by value — the function gets a copy. Pass a pointer if you want the function to mutate the original.

go playground
Loading...

nil pointers

A pointer's zero value is nil. Dereferencing nil is a runtime panic ("invalid memory address"), so always check before dereferencing untrusted pointers — usually the function that returns the pointer also returns an error you should be checking first.

var p *int
if p != nil {
    fmt.Println(*p)
}

new(T)

new(T) allocates zero-valued storage for a T and returns a pointer to it:

p := new(int)   // *int pointing at a fresh 0
*p = 42

In practice new is rare. You'll more often write &MyStruct{...} which is both shorter and lets you set fields at the same time.

Pointers vs references

Go has no & references like C++. Variables are either a value or a pointer — not a reference. That said:

  • Slices, maps, channels, functions, and interfaces internally contain pointers to a heap structure. So even when you pass them "by value", the underlying data is shared. That's why a function can mutate a slice's elements but not change its length as seen by the caller.

When should I use a pointer?

A rough rule of thumb:

  • Use a pointer when the function needs to mutate the value, when the value is large (avoid copying), or when nil is a meaningful "not set" state.
  • Use a value for small, immutable things (an int, a small struct, a time.Time). Copies are cheap and the code is simpler.

Don't reach for pointers just because you can — passing a small struct by value is often faster than chasing a pointer through memory.

Escape analysis

You'll hear about "stack vs heap" in Go. The good news: you almost never decide where memory lives. The compiler runs escape analysis and puts data on the heap only if it outlives the function (e.g., you return a pointer to a local variable — perfectly legal in Go, unlike C).

func newCounter() *int {
    n := 0
    return &n   // n escapes to the heap — totally safe
}

Run go build -gcflags="-m" to see what the compiler decided.

What is the zero value of a `*int`?