Pointers
Pointers store memory addresses of values. They're essential for efficient programming and understanding how Go manages memory. In this lesson, you'll learn what pointers are, how to use them, and when they're necessary.
What is a Pointer?
A pointer holds the memory address of a value:
Click Run to execute your code
&- Address operator (gets the address of a variable)*- Dereference operator (accesses the value at an address)*Type- Pointer type declaration
Memory Visualization
x := 42
p := &x
// Memory layout:
// Variable x: [42] at address 0x1234
// Variable p: [0x1234] (stores address of x)
//
// *p accesses the value at address 0x1234, which is 42
Pointer Basics
Declaring Pointers
// Declare a pointer to int
var p *int
// Get address of a variable
x := 42
p = &x
// Declare and initialize
y := 100
ptr := &y
// Zero value of a pointer is nil
var nilPtr *int // nil
Dereferencing Pointers
Click Run to execute your code
Pointers and Functions
Pointers allow functions to modify the original value:
Click Run to execute your code
| Pass By Value | Pass By Pointer |
|---|---|
| Copies the value | Copies the address |
| Cannot modify original | Can modify original |
| Expensive for large structs | Cheap (just an address) |
func f(x int) |
func f(x *int) |
Figure: Pointers - Understanding memory addresses, address-of operator (&), and dereference operator (*)
Pointers to Structs
Pointers are commonly used with structs:
type Person struct {
Name string
Age int
}
// Create a struct
p := Person{Name: "Alice", Age: 25}
// Get pointer to struct
ptr := &p
// Access fields (Go automatically dereferences)
fmt.Println(ptr.Name) // Same as (*ptr).Name
// Modify through pointer
ptr.Age = 26
fmt.Println(p.Age) // 26 (original modified)
ptr.Name is shorthand for
(*ptr).Name.
new() Function
// new() allocates memory and returns a pointer
p := new(Person) // *Person, all fields zero-valued
p.Name = "Bob"
p.Age = 30
// Equivalent to:
p := &Person{} // More common idiom
When to Use Pointers?
โ Use Pointers When:
- You need to modify the original value
func increment(x *int) { *x++ } - Working with large structs (avoid copying)
type LargeStruct struct { data [1000000]int } func process(s *LargeStruct) { // Efficient: only copies pointer } - Need to represent "no value" (nil)
var optionalValue *int // nil means "no value" - Implementing methods that modify the receiver
func (p *Person) Birthday() { p.Age++ }
โ Don't Use Pointers When:
- Working with small values (int, bool, small structs)
- Slices, maps, channels (already reference types)
- You don't need to modify the value
Common Pointer Patterns
1. Returning Pointers
// Safe: Go handles memory automatically
func newPerson(name string) *Person {
p := Person{Name: name}
return &p // Safe! Go moves to heap if needed
}
// Usage
person := newPerson("Alice")
2. Pointer to Pointer
x := 42
p := &x // *int
pp := &p // **int (pointer to pointer)
fmt.Println(**pp) // 42
3. Nil Pointer Checks
func process(p *Person) {
if p == nil {
fmt.Println("nil pointer")
return
}
fmt.Println(p.Name)
}
Common Mistakes
1. Dereferencing nil pointer
// โ Wrong - panic!
var p *int
*p = 42 // panic: nil pointer dereference
// โ
Correct - initialize first
x := 0
p := &x
*p = 42
2. Taking address of loop variable
// โ Wrong - all pointers point to same variable
var ptrs []*int
for _, v := range []int{1, 2, 3} {
ptrs = append(ptrs, &v) // All point to same v!
}
// โ
Correct - create new variable
var ptrs []*int
for _, v := range []int{1, 2, 3} {
v := v // Create new variable
ptrs = append(ptrs, &v)
}
3. Unnecessary pointer to slice/map
// โ Wrong - slices are already references
func addItem(s *[]int, item int) {
*s = append(*s, item)
}
// โ
Correct - slices don't need pointers
func addItem(s []int, item int) []int {
return append(s, item)
}
Exercise: Swap Function
Task: Create a swap function using pointers.
Requirements:
- Create a
swapfunction that swaps two integers - Use pointers to modify the original values
- Test with values 10 and 20
- Print before and after values
Show Solution
package main
import "fmt"
// swap exchanges the values of two integers
func swap(a, b *int) {
*a, *b = *b, *a
}
// swapStrings exchanges two strings
func swapStrings(a, b *string) {
*a, *b = *b, *a
}
func main() {
// Test with integers
x, y := 10, 20
fmt.Printf("Before swap: x=%d, y=%d\n", x, y)
swap(&x, &y)
fmt.Printf("After swap: x=%d, y=%d\n", x, y)
// Test with strings
s1, s2 := "Hello", "World"
fmt.Printf("\nBefore swap: s1=%s, s2=%s\n", s1, s2)
swapStrings(&s1, &s2)
fmt.Printf("After swap: s1=%s, s2=%s\n", s1, s2)
// Bonus: Demonstrate why pointers are needed
fmt.Println("\n--- Without pointers (doesn't work) ---")
a, b := 100, 200
fmt.Printf("Before: a=%d, b=%d\n", a, b)
// This won't work (copies values)
func(x, y int) {
x, y = y, x // Only swaps copies!
}(a, b)
fmt.Printf("After: a=%d, b=%d (unchanged!)\n", a, b)
}
Summary
- Pointers store memory addresses of values
- & operator gets the address of a variable
- * operator dereferences (accesses value at address)
- nil is the zero value for pointers
- Use pointers to modify original values in functions
- Automatic dereferencing for struct field access
- new() allocates and returns a pointer
- Don't use pointers for slices, maps, or channels
What's Next?
Now that you understand pointers, you're ready to learn about InterfacesโGo's powerful way to define behavior and achieve polymorphism without inheritance!
Enjoying these tutorials?