Understanding Pointers in Go: From Basic Concepts to unsafe Package

Regular Pointers

Pointers in Go are specialized varible types that store the memory address of another variable. While not as frequently used as in C or C++, pointers remain essential for modifying variables within functions, avoiding expensive large object copies, and implementing data structures like linked lists and trees.

Fundamental Concepts

  • Memory Address: Every variable occupies memory space with a unique address. A pointer variable stores this address.
  • Dereferencing: Accessing the value at the address a pointer holds is called dereferencing.

Declaring Pointers

Pointer declaration uses the * operator before the type:

var count *int

Here, count is a pointer to an int value.

Initializing Pointers

Pointers must be initialized before use. The & operator retrieves a variable's address:

number := 15
address := &number

In this example, address holds the memory location of variable.

Dereferencing Pointers

The * operator dereferences a pointer to access or modify its underlying value:

*address = 25

This changes number to 25.

Pointers in Functions

Passing pointers to functions enables modification of external variables:

func updateValue(ptr *int) {
    *ptr = 45
}

func main() {
    number := 15
    updateValue(&number)
    fmt.Println(number)
}

The updateValue function modifies number through its pointer parameter.

Nil Pointers

Uninitialized pointers default to nil, indicating they reference no address:

var count *int
if count == nil {
    fmt.Println("Pointer is uninitialized")
}

Pointers and Structs

Pointers work naturally with structs for creating and modifying struct instances:

type Employee struct {
    Name   string
    Salary int
}

func main() {
    staff := &Employee{Name: "Bob", Salary: 5000}
    staff.Salary = 5500
}

Pointer Arrays vs Array Pointers

  • Pointer Array: An array containing pointer elements
var ptrSlice [4]*string
  • Array Pointer: A pointer pointing to an entire array
var items [4]string
var arrayRef *[4]string = &items

Pointers to Pointers

Go supports multiple levels of indirection:

num := 200
ptr := &num
doubleRef := &ptr

Here, doubleRef points to ptr, which points to num.

Important Considerations

  • Go prohibits pointer arithmetic—you cannot add or subtract from pointers.
  • The garbage collector automatically manages memory, eliminating the need for manual deallocation.

unsafe.Pointer

The unsafe.Pointer type in Go can reference any type's value. The unsafe package bypasses Go's type system, enabling operations like type conversion between pointers and calculating actual memory sizes of objects.

Type Conversion

unsafe.Pointer facilitates conversion between arbitrary pointer types:

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    val := 36
    intRef := &val
    genericPtr := unsafe.Pointer(intRef)
    rawAddr := uintptr(genericPtr)

    genericPtr = unsafe.Pointer(rawAddr)
    converted := (*int)(genericPtr)
    *converted = 72

    fmt.Println(val)
}

Accessing Arbitrary Memory Addresses

unsafe.Pointer can access any memory location, bypassing Go's type safety:

target := unsafe.Pointer(uintptr(0xDEADBEEF))

This attempts to access a specific memory address—extremely dangerous and likely to cause undefined behavior or crashes.

Calculating Struct Sizes

unsafe.Sizeof returns byte size of values, often used with unsafe.Pointer for struct analysis:

type Record struct {
    ID   int
    Data string
}

entry := Record{ID: 1, Data: "sample"}
size := unsafe.Sizeof(entry)
fmt.Println(size)

Pointer Arithmetic with uintptr

Go forbids arithmetic on unsafe.Pointer. Convert to uintptr, perform calculations, then convert back:

data := Record{ID: 1, Data: "sample"}
basePtr := unsafe.Pointer(&data)
addr := uintptr(basePtr)
fieldPtr := unsafe.Pointer(addr + unsafe.Offsetof(data.Data))

Usage Guidelines

  • The unsafe package bypasses memory safety guarantees—errors can cause crashes or security vulnerabilities.
  • Limit unsafe usage to essential scenarios and ensure operation safety.
  • unsafe package internals may change between Go versions.

uintptr Type

uintptr is a unsigned integer type in the unsafe package large enough to hold any pointer's bit pattern (memory address). It's designed for low-level programming involving OS interfaces and memory operations.

Key Characteristics

  • uintptr stores the complete bit pattern of any pointer type.
  • Enables bidirectional conversion between pointers and integers.
  • Supports pointer arithmetic while bypassing Go's safety checks.

Practical Example

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    val := 128
    ptr := &val
    addrValue := uintptr(unsafe.Pointer(ptr))

    newLocation := unsafe.Pointer(addrValue + unsafe.Sizeof(val))
    typedPtr := (*int)(newLocation)
    *typedPtr = 256

    fmt.Println(val)
}

Critical Warnings

  • uintptr usage demands extreme caution—improper handling causes memory corruption, out-of-bounds access, and crashes.
  • Never store uintptr values persistently—they become invalid during garbage collection.
  • Direct uintptr usage is rare; reserve it for OS-level or hardware interaction code.

Comparing the Three Pointer Types

Go provides three distinct pointer-related concepts, each serving different purposes in memory operations and type conversion.

Standard Pointers (*Type)

  • Definition: Variables storing another variable's memory address.
  • Purpose: Reference and modify values, pass addresses to functions for in-place updates.
  • Type Safety: Fully type-safe—can only reference specific types.
  • Example:
val := 10
ptr := &val
*ptr = 50

unsafe.Pointer

  • Definition: A special unsafe package type that can reference any type.
  • Purpose: Type conversion between pointers, low-level memory operations.
  • Type Safety: Not inherently safe—it can reference any type but requires accompanying safe pointer usage.
  • Example:
val := 10
ptr := unsafe.Pointer(&val)

uintptr

  • Definition: An unsigned integer type in unsafe package large enough for any pointer's bit pattern.
  • Purpose: Pointer arithmetic, converting pointers to integers for low-level operations.
  • Type Safety: Unsafe—stores arbitrary bit patterns and enables arithmetic that bypasses memory guarantees.
  • Example:
val := 10
ptr := uintptr(unsafe.Pointer(&val))

Comparative Summary

Aspect *Type unsafe.Pointer uintptr
Type Safety High Low None
Primary Use Variable references Type conversion Arithmetic operations
Garbage Collection Safe Manual responsibility Manual responsibility
Arithmetic Support No No Yes

Standard pointers provide type-safe variable access for everyday programming. unsafe.Pointer and uintptr enable low-level operations with significant power but require meticulous handling to maintain program integrity.

Tags: Go pointers unsafe.Pointer uintptr Memory Management

Posted on Fri, 08 May 2026 18:09:50 +0000 by brownca