Pointers Explained
Overview
Pointers hold memory addresses. In Go, they’re simple and safe—no pointer arithmetic allowed.
Pointer Basics
x := 42
p := &x // p is *int, holds address of x
fmt.Println(*p) // 42 (dereference)
*p = 100 // Modify x through pointer
fmt.Println(x) // 100Declaration
var p *int // nil pointer
p = &x // Point to x
// Classic new (zero value)
p := new(int) // *int to 0
// Expression-based new (Go 1.26+)
p := new(42) // *int to 42Stack/heap view (simplified)
x: 42 p: 0xABC
| |
+------ addr ---+
|
[42]
Why Use Pointers
1. Modify Variables in Functions
func increment(n *int) {
*n++
}
x := 1
increment(&x)
fmt.Println(x) // 22. Avoid Copying Large Structs
type BigStruct struct {
Data [1000]int
}
// Bad: copies entire struct
func process(b BigStruct) { }
// Good: passes pointer (8 bytes)
func process(b *BigStruct) { }nil Pointers
var p *int // nil
if p != nil {
fmt.Println(*p) // Safe
}
// Dereferencing nil panics!
// *p = 1 // panic: runtime errorSafe pattern for optional fields in APIs:
type UpdateUserRequest struct {
Name *string `json:"name,omitempty"`
Email *string `json:"email,omitempty"`
}Pointer to Pointer
x := 42
p := &x
pp := &p // **int
**pp = 100
fmt.Println(x) // 100Pointers and Slices/Maps
Slices and maps are already reference types:
func modify(s []int) {
s[0] = 100 // Modifies original
}
nums := []int{1, 2, 3}
modify(nums)
fmt.Println(nums[0]) // 100
// But reassigning needs pointer
func replace(s *[]int) {
*s = []int{4, 5, 6}
}Common Patterns
Return Pointer for “No Result”
func find(id int) *User {
if found {
return &user
}
return nil // Not found
}
if user := find(1); user != nil {
// Use user
}Constructor Pattern
func NewUser(name string) *User {
return &User{
Name: name,
CreatedAt: time.Now(),
}
}Summary
| Operation | Syntax |
|---|---|
| Get address | &x |
| Dereference | *p |
| Allocate | new(T) |
| Check nil | p != nil |