Pointers

A pointer holds the memory address of a value rather than the value itself. Through a pointer, a function can change a variable that belongs to its caller, and large structs can be shared without copying. Go's pointers are safe — there's no pointer arithmetic and memory is garbage-collected — so they're far less fearsome than in C. This lesson covers & and *, nil pointers, pointers to structs, new, and when to use pointers.

Address and dereference

&x gives the address of x, a value of type *T ("pointer to T"). *p reads or writes the value the pointer points to:

GO
package main

import "fmt"

func main() {
	score := 10
	p := &score // p points to score

	fmt.Printf("%T\n", p)
	fmt.Println(*p) // read through the pointer

	*p = 25 // write through the pointer
	fmt.Println(score)

	q := p // copying a pointer: both point to score
	*q += 5
	fmt.Println(score, *p, p == q)
}
Output
*int
10
25
30 30 true

Printing p itself would show a hexadecimal address such as 0x14000012345, which differs from run to run.

Changing a caller's variable

Arguments are copied, so to let a function modify a variable, pass its address:

GO
package main

import "fmt"

func double(n *int) {
	*n *= 2
}

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

func main() {
	x := 21
	double(&x)
	fmt.Println(x)

	first, second := "left", "right"
	swap(&first, &second)
	fmt.Println(first, second)
}
Output
42
right left

The same is how fmt.Scan(&name) in lesson 13 fills in your variables.

Pointers to structs

Pointers are most often used with structs. Go lets you access fields through a pointer without writing the *: p.Name is shorthand for (*p).Name:

GO
package main

import "fmt"

type Player struct {
	Name  string
	Score int
}

func addPoints(p *Player, pts int) {
	p.Score += pts // automatic dereference
}

func main() {
	ana := Player{Name: "Ana"}
	addPoints(&ana, 10)
	addPoints(&ana, 5)
	fmt.Println(ana.Name, ana.Score)

	ben := &Player{Name: "Ben", Score: 3} // a pointer to a new struct
	addPoints(ben, 7)
	fmt.Printf("%+v\n", *ben)
}
Output
Ana 15
{Name:Ben Score:10}

nil pointers

The zero value of a pointer is nil: it points to nothing. Dereferencing a nil pointer panics, so check pointers that might be nil:

GO
package main

import "fmt"

type Config struct{ Port int }

func port(c *Config) int {
	if c == nil {
		return 8080 // a sensible default
	}
	return c.Port
}

func main() {
	fmt.Println(port(&Config{Port: 9000}), port(nil))

	var c *Config
	fmt.Println(c.Port) // nil dereference
}
Output
9000 8080
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x104d4f5a4]

goroutine 1 [running]:
…
exit status 2

Nil pointer dereferences are the most common panic in Go programs. A pointer is often used precisely to mean "this value is optional", so make the nil case part of your design.

new

The built-in new allocates a variable and returns a pointer to it. new(T) gives a pointer to a zero value; since Go 1.26 you can also pass an expression, and the new variable starts with that value:

GO
package main

import "fmt"

type Profile struct {
	Name string
	Age  *int // optional: nil means "unknown"
}

func main() {
	n := new(int) // *int pointing to 0
	*n = 3
	fmt.Println(*n)

	withAge := Profile{Name: "Ana", Age: new(34)}
	noAge := Profile{Name: "Ben"}
	for _, p := range []Profile{withAge, noAge} {
		if p.Age != nil {
			fmt.Println(p.Name, *p.Age)
		} else {
			fmt.Println(p.Name, "age unknown")
		}
	}
}
Output
3
Ana 34
Ben age unknown

Before Go 1.26, filling an optional pointer field like Age needed a temporary variable (age := 34; p.Age = &age) or a helper function. new(34) does it in one expression.

Returning pointers to local variables

In C, returning the address of a local variable is a serious bug. In Go it's perfectly safe: the compiler notices that the variable outlives the function and allocates it on the garbage-collected heap instead of the stack:

GO
package main

import "fmt"

type Counter struct{ n int }

func newCounter(start int) *Counter {
	c := Counter{n: start}
	return &c // safe: c lives on after the function returns
}

func main() {
	c := newCounter(5)
	c.n++
	fmt.Println(c.n)
}
Output
6

When to use pointers

Use a pointer when…Example
A function must modify the caller's valuefunc (a *Account) Deposit(n int)
The struct is large and copying it would be wastefulStructs with many fields or big arrays
The value must be shared, with one identityA database connection pool, a cache
"No value" must be distinguishable from a zero valueOptional fields: *int, *time.Time

Don't use pointers for small values that aren't modified, and never to slices, maps or channels just to "avoid copying" — those are already small headers referring to shared data.

No pointer arithmetic

Unlike C, you can't add to a pointer to move through memory: p++ on a pointer doesn't compile. Use slices and indexes instead. (The unsafe package exists for rare low-level needs, but as its name warns, it gives up Go's safety guarantees.)

Try it yourself

Write applyRaise, which gives every employee in a slice a percentage raise. Should it take []Employee or []*Employee? Try both.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
Ana 54080
Ben 49400
Next: attaching behaviour to types with methods.