Skip to content
AgileLogics
6 min read

Pointers

Prerequisites: Structs

A pointer holds the memory address of a value. Pointers let a function change a variable that belongs to its caller, and avoid copying large structs.

Syntax

x := 5
p := &x      // & takes the address: p has type *int
fmt.Println(*p) // * dereferences: reads the value at the address
*p = 10      // writes through the pointer — x is now 10

q := new(int) // allocates a zero int and returns its address
var r *int    // a pointer's zero value is nil (points nowhere)

Example

package main

import "fmt"

type Player struct {
    Name  string
    Score int
}

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

func addPoints(p *Player, points int) {
    p.Score += points // same as (*p).Score
}

func main() {
    x := 5
    p := &x
    fmt.Println(*p)

    *p = 10
    fmt.Println(x)

    double(&x)
    fmt.Println(x)

    counter := new(int)
    *counter++
    fmt.Println(*counter)

    var missing *int
    fmt.Println(missing == nil)

    player := Player{Name: "Ada", Score: 10}
    addPoints(&player, 5)
    fmt.Println(player.Score)

    hero := &Player{Name: "Hero"} // pointer to a new struct
    hero.Score = 99
    fmt.Printf("%+v\n", *hero)
}

Expected output

5
10
20
1
true
15
{Name:Hero Score:99}

Things that make Go pointers simpler than C pointers:

  • No pointer arithmetic — you can't do p + 1 to move to the next address, which removes a whole class of memory bugs.
  • Automatic dereferencing for struct fields — p.Score works on a *Player without writing (*p).Score.
  • Garbage collected — it's safe to return a pointer to a local variable from a function; Go keeps the value alive as long as something points to it.

Printing a pointer itself (fmt.Println(p)) shows an address such as 0xc000012345, which differs from run to run.

Common mistakes

  • Dereferencing a nil pointer. This compiles but crashes: panic: runtime error: invalid memory address or nil pointer dereference. Check if p != nil when a pointer might be empty.
  • Mixing up the two uses of *: in a type (*int) it means "pointer to"; in an expression (*p) it means "the value pointed to".
  • Using pointers everywhere "for speed". Small values such as ints and small structs are cheap to copy; use a pointer when you need to share or modify.

Practice

Write swap(a, b *int) that exchanges the values of two int variables. Call it on x := 1 and y := 2 and print them.

Show solution
package main

import "fmt"

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

func main() {
    x, y := 1, 2
    swap(&x, &y)
    fmt.Println(x, y) // 2 1
}