Go Pointers

Memory addresses and references in Go programming

👉 What are Go Pointers?

Pointers store memory addresses of variables instead of values. They allow you to directly access and modify data in memory, enabling efficient data sharing and manipulation.


// Create a pointer
x := 42
ptr := &x        // ptr holds address of x
fmt.Println(*ptr) // Output: 42 (value at address)
                                    

Output:

42

Key Pointer Concepts

📍

Memory Address

Pointers store memory locations

x := 10
ptr := &x  // ptr = address of x
🔍

Dereferencing

Access values through pointers

value := *ptr  // Get value at address
🔄

Direct Modification

Change original values via pointers

*ptr = 20  // Changes original variable

Efficiency

Pass large data without copying

func modify(ptr *BigStruct) {
    // No copying needed
}

🔹 Basic Pointer Operations

Understanding address-of (&) and dereference (*) operators:

package main

import "fmt"

func main() {
    // Regular variable
    number := 42
    fmt.Println("Value of number:", number)
    fmt.Println("Address of number:", &number)
    
    // Create a pointer
    ptr := &number
    fmt.Println("Pointer ptr holds address:", ptr)
    fmt.Println("Value at address (dereference):", *ptr)
    
    // Modify through pointer
    *ptr = 100
    fmt.Println("After modifying through pointer:")
    fmt.Println("number is now:", number)
    fmt.Println("*ptr is now:", *ptr)
    
    // Pointer to pointer
    ptrToPtr := &ptr
    fmt.Println("Address of ptr:", ptrToPtr)
    fmt.Println("Value through double dereference:", **ptrToPtr)
}

Output:

Value of number: 42
Address of number: 0xc0000140b8
Pointer ptr holds address: 0xc0000140b8
Value at address (dereference): 42
After modifying through pointer:
number is now: 100
*ptr is now: 100
Address of ptr: 0xc000006028
Value through double dereference: 100

🔹 Pointers with Functions

Use pointers to modify variables inside functions:

package main

import "fmt"

// Function with value parameter (creates copy)
func doubleValue(x int) {
    x = x * 2
    fmt.Println("Inside doubleValue:", x)
}

// Function with pointer parameter (modifies original)
func doublePointer(x *int) {
    *x = *x * 2
    fmt.Println("Inside doublePointer:", *x)
}

// Function that returns a pointer
func createPointer(value int) *int {
    return &value
}

func main() {
    number := 10
    fmt.Println("Original number:", number)
    
    // Pass by value - original unchanged
    doubleValue(number)
    fmt.Println("After doubleValue:", number)
    
    // Pass by pointer - original modified
    doublePointer(&number)
    fmt.Println("After doublePointer:", number)
    
    // Get pointer from function
    ptr := createPointer(50)
    fmt.Println("Pointer from function:", *ptr)
}

Output:

Original number: 10
Inside doubleValue: 20
After doubleValue: 10
Inside doublePointer: 20
After doublePointer: 20
Pointer from function: 50

🔹 Pointers with Structs

Pointers are commonly used with structs for efficiency:

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

// Method with pointer receiver
func (p *Person) Birthday() {
    p.Age++
    fmt.Printf("%s is now %d years old\n", p.Name, p.Age)
}

// Function that takes pointer to struct
func updateName(p *Person, newName string) {
    p.Name = newName
}

func main() {
    // Create struct
    person := Person{Name: "Alice", Age: 25}
    fmt.Println("Original:", person)
    
    // Get pointer to struct
    personPtr := &person
    
    // Access fields through pointer (automatic dereferencing)
    fmt.Println("Name through pointer:", personPtr.Name)
    fmt.Println("Age through pointer:", personPtr.Age)
    
    // Modify through pointer
    personPtr.Name = "Alice Smith"
    personPtr.Age = 26
    fmt.Println("After modification:", person)
    
    // Call method (automatically takes address)
    person.Birthday()
    
    // Pass pointer to function
    updateName(&person, "Alice Johnson")
    fmt.Println("After name update:", person)
    
    // Create pointer directly
    anotherPerson := &Person{Name: "Bob", Age: 30}
    fmt.Println("Direct pointer creation:", *anotherPerson)
}

Output:

Original: {Alice 25}
Name through pointer: Alice
Age through pointer: 25
After modification: {Alice Smith 26}
Alice Smith is now 27 years old
After name update: {Alice Johnson 27}
Direct pointer creation: {Bob 30}

🔹 Nil Pointers and Safety

Understanding nil pointers and safe pointer usage:

package main

import "fmt"

func main() {
    // Nil pointer
    var ptr *int
    fmt.Println("Nil pointer:", ptr)
    fmt.Println("Is nil?", ptr == nil)
    
    // Safe pointer check before dereferencing
    if ptr != nil {
        fmt.Println("Value:", *ptr)
    } else {
        fmt.Println("Cannot dereference nil pointer")
    }
    
    // Create valid pointer
    number := 42
    ptr = &number
    fmt.Println("Valid pointer:", ptr)
    fmt.Println("Is nil?", ptr == nil)
    
    if ptr != nil {
        fmt.Println("Safe to dereference:", *ptr)
    }
    
    // Using new() function
    newPtr := new(int)  // Creates pointer to zero value
    fmt.Println("New pointer:", newPtr)
    fmt.Println("Value at new pointer:", *newPtr)
    
    *newPtr = 100
    fmt.Println("After assignment:", *newPtr)
}

Output:

Nil pointer: <nil>
Is nil? true
Cannot dereference nil pointer
Valid pointer: 0xc0000140b8
Is nil? false
Safe to dereference: 42
New pointer: 0xc0000140c0
Value at new pointer: 0
After assignment: 100

🧠 Test Your Knowledge

Which operator is used to get the address of a variable in Go?