Pointers
A pointer holds the memory address of a value. Go pointers give you control over sharing and mutation without the complexity of manual memory management — the garbage collector handles deallocation automatically.
The & and * Operators
&x— takes the address ofx, producing a*T(pointer to T).*p— dereferences pointerp, giving you the value it points to.
package main
import "fmt"
func main() {
x := 42
p := &x // p is *int
fmt.Println(x) // 42
fmt.Println(p) // 0xc0000b4008 (some memory address)
fmt.Println(*p) // 42
*p = 100 // modify x through p
fmt.Println(x) // 100
}Why Pointers Matter
Functions receive arguments by value — a copy is made. Use a pointer to let a function modify the caller's variable:
package main
import "fmt"
func doubleValue(n int) {
n *= 2 // modifies the copy only
}
func doublePointer(n *int) {
*n *= 2 // modifies the original
}
func main() {
x := 5
doubleValue(x)
fmt.Println(x) // 5 — unchanged
doublePointer(&x)
fmt.Println(x) // 10 — modified
}Pointer Receivers
Pointer receivers allow methods to modify their receiver and are more efficient for large structs:
package main
import "fmt"
type Stack struct {
items []int
}
func (s *Stack) Push(v int) {
s.items = append(s.items, v)
}
func (s *Stack) Pop() (int, bool) {
if len(s.items) == 0 {
return 0, false
}
last := len(s.items) - 1
v := s.items[last]
s.items = s.items[:last]
return v, true
}
func (s Stack) Len() int {
return len(s.items)
}
func main() {
var st Stack
st.Push(1)
st.Push(2)
st.Push(3)
fmt.Println(st.Len()) // 3
if v, ok := st.Pop(); ok {
fmt.Println("popped:", v) // popped: 3
}
fmt.Println(st.Len()) // 2
}Idiomatic Go: Go automatically takes the address when you call a pointer-method on an addressable value.
st.Push(1)and(&st).Push(1)are equivalent.
The new Function
new(T) allocates a zeroed T and returns a *T. It is less common than composite literals with &:
package main
import "fmt"
type Config struct {
Debug bool
Workers int
}
func main() {
c1 := new(Config) // &Config{false, 0}
c2 := &Config{Debug: true, Workers: 4} // idiomatic
fmt.Println(*c1) // {false 0}
fmt.Println(*c2) // {true 4}
}Nil Pointers
A pointer's zero value is nil. Dereferencing nil causes a panic — always check before use:
package main
import "fmt"
type Node struct {
Value int
Next *Node
}
func printList(n *Node) {
for n != nil {
fmt.Print(n.Value, " ")
n = n.Next
}
fmt.Println()
}
func main() {
head := &Node{Value: 1, Next: &Node{Value: 2, Next: &Node{Value: 3}}}
printList(head) // 1 2 3
}Key Takeaways
&xtakes the address ofx;*pdereferences a pointer.- Pass pointers when a function needs to modify the caller's value or to avoid copying large structs.
- Go automatically takes the address when calling pointer-method on an addressable value.
- The zero value of a pointer is
nil; dereferencingnilpanics. - The garbage collector manages memory — you do not call
free.