DEV Community

Huseyn
Huseyn

Posted on

Pointers in Go

What is a Pointer?

In Go, a pointer is a variable that** stores the memory address** of another variable. Instead of holding a value directly, it points to the location where the value is stored.

Syntax

var x int = 10
var p *int = &x // p is a pointer to x
Enter fullscreen mode Exit fullscreen mode
  • &x: address-of operator — gets the memory address of variable x.
  • *p: dereference operator — gets the value from the address p is pointing to.

Why Use Pointers?

  • To share data across function calls without copying.
  • To modify variables inside functions.
  • To build data structures like linked lists, trees, etc.
  • To optimize memory usage and performance by avoiding unnecessary copies.

Declaring and Using Pointers

var a int = 42
var p *int = &a

fmt.Println("Value of a:", a)   // 42
fmt.Println("Address of a:", &a)
fmt.Println("Value of p:", p)   // address of a
fmt.Println("Value at p:", *p)  // 42
Enter fullscreen mode Exit fullscreen mode

Pointer Zero Value
The zero value of a pointer is nil.

var p *int
fmt.Println(p) // <nil>
Enter fullscreen mode Exit fullscreen mode

Always check for nil before dereferencing a pointer:

if p != nil {
    fmt.Println(*p)
}
Enter fullscreen mode Exit fullscreen mode

Passing Pointers to Functions

By Value (copy)

func increment(n int) {
    n++
}
Enter fullscreen mode Exit fullscreen mode

This won't affect the original value.

By Pointer (reference)

func incrementPtr(n *int) {
    *n++
}

x := 5
incrementPtr(&x)
fmt.Println(x) // 6
Enter fullscreen mode Exit fullscreen mode

Returning Pointers from Functions
It's safe to return pointers to local variables in Go — the compiler allocates them on the heap if necessary.

func createPointer() *int {
    x := 100
    return &x
}
Enter fullscreen mode Exit fullscreen mode

Structs and Pointers

Value Receiver

type User struct {
    Name string
}

func (u User) Update(name string) {
    u.Name = name // Doesn't affect the original
}

Enter fullscreen mode Exit fullscreen mode

Pointer Receiver

func (u *User) Update(name string) {
    u.Name = name // Modifies the original
}
Enter fullscreen mode Exit fullscreen mode

Calling pointer receivers on values (and vice versa)
Go automatically converts between values and pointers when calling methods:

u := User{}
u.Update("Alice") // works even if Update expects *User
Enter fullscreen mode Exit fullscreen mode

Pointers and Arrays/Slices
Arrays are value types

func modifyArray(arr [3]int) {
    arr[0] = 100
}
Enter fullscreen mode Exit fullscreen mode

Won't affect original array.

Use a pointer to modify array

func modifyArrayPtr(arr *[3]int) {
    arr[0] = 100
}
Enter fullscreen mode Exit fullscreen mode

Slices behave like pointers internally
You can modify slice contents without using explicit pointers:

func modifySlice(s []int) {
    s[0] = 100 // modifies original slice
}
Enter fullscreen mode Exit fullscreen mode

Pointers to Pointers
Go supports pointers to pointers, but they’re rarely needed in idiomatic Go.

var x int = 10
var p *int = &x
var pp **int = &p

fmt.Println(**pp) // 10

Enter fullscreen mode Exit fullscreen mode

Interface and Pointer Subtleties
Value vs Pointer receivers in interfaces

type Printer interface {
    Print()
}

type Data struct{}

func (d Data) Print()    { fmt.Println("Value") }
func (d *Data) Print()   { fmt.Println("Pointer") }

func main() {
    var d Data
    var p Printer = &d // works
    // var q Printer = d // doesn't work if only pointer receiver is defined
}
Enter fullscreen mode Exit fullscreen mode

Always ensure your receiver type matches your intended interface use.

Pointers and new Keyword
new(Type) allocates zeroed storage and returns a pointer.

p := new(int) // *int, initialized to 0
*p = 10
Enter fullscreen mode Exit fullscreen mode

Equivalent to:

var x int
p := &x
Enter fullscreen mode Exit fullscreen mode

Pointers and Garbage Collection
Go has garbage collection. You don’t need to free memory manually. Once a pointer is no longer referenced, Go will reclaim the memory.

Best Practices

  • Use pointers to avoid copying large structs.
  • Use pointer receivers if your method modifies the receiver or avoids copying.
  • Avoid pointer-to-interface — use interfaces directly.
  • Be careful with nil — always check before dereferencing.
  • Prefer slices, maps, and channels over manual pointer-heavy code when possible.
  • Avoid using pointers with basic types (like int, bool) unless necessary.

Common Mistakes

Mistake Explanation
Dereferencing a nil pointer Causes runtime panic. Always check for nil.
Using a pointer to a value that goes out of scope in a goroutine Safe in Go, but understand escape analysis.
Modifying value instead of pointer in struct method Use pointer receiver if modification is needed.
Pointer to interface Avoid — interface already holds type and value.

Pointer Comparison

a := 100
b := 100
p1 := &a
p2 := &a
p3 := &b

fmt.Println(p1 == p2) // true
fmt.Println(p1 == p3) // false

Enter fullscreen mode Exit fullscreen mode

Pointers are equal if they point to the same memory address.

Example: Swap Function

func swap(a, b *int) {
    *a, *b = *b, *a
}

x, y := 1, 2
swap(&x, &y)
fmt.Println(x, y) // 2, 1

Enter fullscreen mode Exit fullscreen mode

Top comments (0)