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

  1. Use var for zero-value defaults: Cleaner than explicit initialization.
  2. Prefer stack allocation: Use small structs/value types for hot code paths.
  3. Constants aren’t just for literals: They prevent heap allocations (data segment = no GC overhead).