Pointers
What is a pointer?
Section titled “What is a pointer?”A pointer is a variable that holds the memory address of another variable. The type of a pointer to T is written *T.
x := 42p := &x // & takes the address of x; p has type *intTo read or write through a pointer, dereference it with *:
fmt.Println(*p) // 42 — read through p*p = 100 // write through p — x is now 100fmt.Println(x) // 100& and * are inverse operations
Section titled “& and * are inverse operations”| Operator | Reads as | Effect |
|---|---|---|
&x | ”address of x” | produces a *T pointing to x |
*p | ”value at p” | dereferences p, reads or writes T |
When to use pointers
Section titled “When to use pointers”1. Mutation across function boundaries. Go passes all arguments by value. If a function needs to modify the caller’s variable, pass a pointer:
func double(n *int) { *n = *n * 2}x := 5double(&x)fmt.Println(x) // 102. Large structs. Copying a large struct on every call is wasteful. Pass *Config instead of Config when the struct has many fields.
3. Optional values. A *T can be nil to represent “not set”, similar to null in other languages.
Struct pointer shorthand
Section titled “Struct pointer shorthand”When you have a pointer to a struct, Go automatically dereferences it for field access. You do not need to write (*cfg).Debug:
cfg := &Config{Workers: 4}cfg.Debug = true // shorthand for (*cfg).Debug = truenew allocates a zeroed value
Section titled “new allocates a zeroed value”The built-in new(T) allocates a zero value of type T and returns *T:
n := new(int) // *int pointing to 0*n = 7Nil pointers
Section titled “Nil pointers”The zero value of any pointer type is nil. Dereferencing a nil pointer causes a runtime panic:
var p *intfmt.Println(p == nil) // true// *p = 1 would panic: nil pointer dereferenceAlways check a pointer for nil before dereferencing when it might not have been set.
No pointer arithmetic
Section titled “No pointer arithmetic”Go deliberately omits pointer arithmetic. You cannot do p++ or p + 4 to walk through memory. This eliminates an entire class of memory-safety bugs. The unsafe package provides pointer arithmetic as an escape hatch, but it is almost never needed in application code.
package main
import "fmt"
type Config struct { Debug bool Workers int}
func enableDebug(cfg *Config) { cfg.Debug = true // mutates the caller's Config}
func double(n *int) { *n = *n * 2}
func main() { // & takes the address; * dereferences x := 42 p := &x fmt.Println(*p) // 42 *p = 100 fmt.Println(x) // 100 -- x was mutated through p
// Pointer for mutation across function boundary double(&x) fmt.Println(x) // 200
// Pointer to struct cfg := Config{Debug: false, Workers: 4} enableDebug(&cfg) fmt.Println(cfg.Debug) // true
// new() allocates a zeroed value and returns a pointer n := new(int) *n = 7 fmt.Println(*n) // 7
// nil pointer var ptr *int fmt.Println(ptr == nil) // true}Loading Go runtime (first run only, ~8 MB)…