Pointers
Pointers hold memory addresses of values.
Introduction
Pointers hold memory addresses of values. Go has pointers but no pointer arithmetic — safer than C. Use pointers to mutate function arguments, avoid copying large structs, and represent optional values (nil pointer = absent).
Understanding & (address-of) and * (dereference), nil pointer checks, and when Go automatically dereferences (p.Field equivalent to (*p).Field) prevents panics and clarifies method receiver choices.
Interviewers ask pointer vs value semantics, escape analysis hints, and why Go doesn't have pass-by-reference — this lesson answers all with production patterns.
The story
A Kubernetes controller passes a pointer to a Deployment spec so the reconciliation loop mutates the object in place before sending a PATCH to the API server. Without pointers, Go copies the entire struct on every function call — expensive for large CRD objects and incorrect when the callee must modify shared state.
Understanding & (take address) and * (dereference) is essential for JSON unmarshaling into structs, linked lists, and shared counters guarded by mutexes.
Understanding the topic
Key concepts
- &x takes address of x; *p dereferences pointer p.
- Zero value of pointer is nil — dereferencing nil panics.
- new(T) allocates zero value on heap, returns *T.
- Go passes by value — pointer copy shares underlying data.
- No pointer arithmetic — cannot iterate with ptr++.
- Optional field: *string nil means absent in JSON with omitempty.
Step-by-step explanation
- Declare: var p *int; p = &value.
- Dereference: *p = 42 modifies pointed-to value.
- Struct field access auto-dereferences: p.Name works for *Person.
- new and & literals allocate; escape analysis may stack-allocate.
- Compare pointers with == (same address) or reflect.DeepEqual for content.
- Return pointer from function — escapes to heap, GC manages lifetime.
Practical code example
Optional JSON field with pointer and in-place mutation via pointer param:
package mainimport ("encoding/json""fmt")type Profile struct {Name string `json:"name"`Nickname *string `json:"nickname,omitempty"`}func setDefault(n *int, defaultVal int) {if *n == 0 {*n = defaultVal}}func main() {nick := "Al"p := Profile{Name: "Alice", Nickname: &nick}data, _ := json.Marshal(p)fmt.Println(string(data))p2 := Profile{Name: "Bob"}data2, _ := json.Marshal(p2)fmt.Println(string(data2))count := 0setDefault(&count, 10)fmt.Println("count:", count)}
Output
{"name":"Alice","nickname":"Al"}
{"name":"Bob"}
count: 10Line-by-line code explanation
func Update(d *Deployment)accepts a pointer so mutations affect the caller's struct.d.Replicas = 3modifies the original — no return value needed.ptr := &deploymenttakes the address of a variable.value := *ptrdereferences the pointer to read the underlying value.new(int)allocates on the heap and returns*int— rarely needed vs&.nil pointerdereference panics — always validate pointers from external APIs.json.Unmarshal(data, &cfg)requires a pointer so the decoder can populate fields.pointer vs value— small structs (≤64 bytes) may be cheaper by value; large or mutable structs use pointers.
Key takeaway: *string with omitempty omits nil from JSON. Pointer params enable mutation. Always nil-check before dereference in defensive code.
Real-world use
Where you'll use this in production
- Optional API fields in JSON request/response structs.
- Mutating function arguments without return values.
- Linked lists and tree structures in algorithms.
- Sharing large struct state across goroutines via pointer.
Best practices
- Nil-check pointers from external sources before dereference.
- Use pointers for optional fields in JSON/API structs.
- Return *T from constructors for consistency with pointer receivers.
- Don't use pointer to slice/map — they're already reference-like.
- Avoid unnecessary pointers to small structs — copy cheaper.
Common mistakes
- Dereferencing nil pointer — runtime panic.
- Pointer to loop variable — all pointers same address; use local copy.
- *&x is just x — redundant indirection.
- Returning pointer to local variable — safe in Go (escapes to heap).
- Pointer to interface — almost never needed.
Advanced interview questions
Q1BeginnerPass by reference in Go?
Q2Beginnernew vs & literal?
Q3IntermediateWhen use pointer to struct?
Q4IntermediatePointer to slice necessary?
Q5AdvancedExplain nil pointer in interface vs nil interface.
Summary
Pointers enable mutation and optional values; nil means absent. No pointer arithmetic — safer than C/C++. Always nil-check before dereferencing external pointers. Don't pointer-wrap slices and maps unnecessarily. Next lesson: packages — organizing Go code.