A pointer stores the memory address of another value. In Go, pointers are useful when a function needs to modify a caller-owned value, when a type needs a meaningful nil state, or when pointer receiver semantics are appropriate.

Declaring a Pointer

var ptr *int

The zero value of a pointer is nil. Use & to take an address and * to dereference it:

value := 42
ptr := &value

fmt.Println(*ptr) // 42

Modify a Value Through a Pointer

func updateValue(p *int) {
    *p = 100
}

value := 42
updateValue(&value)
fmt.Println(value) // 100

updateValue receives the address of value, so assigning through *p changes the original variable.

Pointer Parameters

A pointer parameter is useful when mutation is part of the function’s contract:

func increment(value *int) {
    (*value)++
}

num := 10
increment(&num)
fmt.Println(num) // 11

Always consider whether mutation makes the API clearer. Returning a new value can be simpler when the function does not need shared mutable state.

Pointers and Struct Methods

Pointer receivers are common when a method modifies a struct or when copying the receiver would be undesirable:

type Counter struct {
    Value int
}

func (c *Counter) Increment() {
    c.Value++
}

counter := Counter{}
counter.Increment()
fmt.Println(counter.Value) // 1

Go automatically takes the address in many ordinary method-call situations, so counter.Increment() works even though Increment has a *Counter receiver.

Pointers Are Not Required for Every Large Value

It is tempting to use pointers purely to avoid copies, but performance depends on escape analysis, allocation behavior, cache locality, and how the value is used. Measure before changing APIs solely for performance.

Slices, maps, channels, functions, and interfaces already contain reference-like descriptors internally and usually do not need a pointer just to share their underlying data.

Nil Pointers

Dereferencing a nil pointer panics:

var ptr *int
fmt.Println(*ptr) // panic

Check for nil when it is a valid state in your API.

Conclusion

Pointers in Go are intentionally simpler than in languages that expose pointer arithmetic. Use them when you need shared mutation, pointer receiver semantics, or an optional value represented by nil. Prefer the clearest ownership and mutation model rather than using pointers automatically everywhere.