Go’s treatment of variables and constants feels almost too simple—until you realize how deliberately it uses memory. Let’s break down where things live (stack, heap, or data segment) and why zero values aren’t just defaults, but a design philosophy.
The Rules in 10 Seconds
- Constants: Stored in the data segment (immutable, compile-time known values).
-
Variables:
- Primitive types (int, bool, etc.): Usually on the stack.
- Pointers/structs: Stack for the pointer, heap for the data (if escaped).
- Zero values: Memory is allocated immediately, even if you don’t assign a value.
Visualizing Memory
+-------------------+
| Data |
|-------------------|
| PI = 3.14 | // const PI = 3.14
| "Open" | // const status = "Open"
+-------------------+
| Stack |
|-------------------|
| x int = 0 | // var x int
| s string = "" | // var s string
| p → Heap | // var p *int = new(int)
+-------------------+
| Heap |
|-------------------|
| 0 (int) | // Allocated for p
+-------------------+
The Code (and What Happens Underneath)
package main
const status = "Open" // Data segment
func main() {
var x int // Stack (zero value = 0)
var s string // Stack (zero value = "")
p := new(int) // p (pointer) on stack, *p (0) on heap
println(x, s, *p) // Output: 0 "" 0
}
Why Zero Values Matter
Go initializes variables to zero values not just to prevent “undefined” chaos, but to guarantee memory safety:
- No garbage values (unlike C).
- Predictable behavior for structs:
type User struct {
ID int // 0
Name string // ""
}
u := User{} // All fields initialized to zero values
Escape Analysis: The Stack/Heap Decider
Run this to see where p
lives:
go build -gcflags="-m" main.go
Output:
./main.go:8:10: new(int) escapes to heap // Heap
Why?
-
new(int)
creates a pointer. - If
p
were returned or passed to another goroutine, the heap allocation is mandatory.
When the Stack Wins vs. Heap
- Stack-friendly:
func add(a, b int) int {
result := a + b // Stays on stack
return result
}
- Heap-forced:
func getUser() *User {
u := User{ID: 42} // Escapes to heap (returned pointer)
return &u
}
Practical Takeaways
-
Use
var
for zero-value defaults: Cleaner than explicit initialization. - Prefer stack allocation: Use small structs/value types for hot code paths.
- Constants aren’t just for literals: They prevent heap allocations (data segment = no GC overhead).