Chapter 3: Pointers & Memory
Understanding how Go manages memory helps you write more efficient code and avoid common pitfalls. Let’s explore pointers, stack vs heap allocation, and escape analysis.
Go manages memory automatically through garbage collection, but understanding pointers and memory allocation helps you write efficient code. Unlike C, Go pointers are safe - no pointer arithmetic, no manual memory management. But unlike high-level languages that hide pointers entirely, Go gives you control when you need it.
This chapter covers pointer fundamentals, when to use pointers vs values, stack vs heap allocation, and escape analysis. You’ll learn how Go decides where to allocate memory and how to write code that works with the runtime rather than against it.
Pointer Basics
Section titled “Pointer Basics”Understanding Pointers
Section titled “Understanding Pointers”A pointer holds the memory address of a value. Use & to get an address and * to dereference it. Pointers let you share data without copying and modify values across function boundaries.
Go pointers are restricted for safety: no pointer arithmetic, no casting arbitrary addresses to pointers. This prevents entire categories of bugs common in C. You get the benefits of pointers without the danger.
When to Use Pointers
Section titled “When to Use Pointers”Use pointers when you need to:
- Modify a value passed to a function
- Avoid copying large structs
- Share data between goroutines
- Signal optional values (nil pointer)
Stack vs Heap
Section titled “Stack vs Heap”Go automatically decides where to allocate memory:
- Stack: Fast, automatic cleanup, limited size
- Heap: Slower, garbage collected, unlimited size
Escape Analysis
Section titled “Escape Analysis”Escape analysis is the compiler pass that decides whether a value can live on the stack or must be heap-allocated. The rule is easy to state - a value escapes if the compiler cannot prove that nothing references it after the function returns - but the answer for any particular line depends on inlining and on the call site. That makes it very easy to guess wrong, so this section shows the compiler’s real verdict rather than intuition.
Nil Pointers
Section titled “Nil Pointers”Always check for nil before dereferencing:
Struct Layout and Memory
Section titled “Struct Layout and Memory”Field order in structs affects memory usage due to alignment:
Slices and Memory
Section titled “Slices and Memory”Slices are already reference types - they contain a pointer to the underlying array:
Key Takeaways
Section titled “Key Takeaways”- Use pointers to modify - pass by pointer when the function needs to change the value
- Use pointers for large structs - avoid copying overhead
- Escape analysis - the compiler decides stack vs heap allocation, per call site and after inlining; read
-gcflags=-minstead of guessing - Check for nil - always validate pointers before use
- Order struct fields - largest to smallest minimizes padding
- Slices are references - they already point to underlying data
Exercise
Section titled “Exercise”Optimize Struct Memory
Given a struct with poor memory layout, reorder its fields to minimize size. Print both the original and optimized struct sizes using unsafe.Sizeof.
Practice
Section titled “Practice”Pointer and allocation decisions show up everywhere once shared state is involved. These batteries exercise them directly:
- Concurrency Exercises - sharing pointers across goroutines safely (start with 1. Goroutine Counter)
- Generics Exercises - 2. Generic Data Structures is a hands-on tour of value vs pointer storage
- Testing Exercises - where
go test -benchmemturns the guesses in this chapter into numbers
Next up: Chapter 4: Goroutines & Channels