Go's unsafe.Pointer Pointer Type

I was trying find the size of a type in bytes and found that the sizeof function of the unsafe package does the trick.

This also led me to stumble upon Go's Pointer type, contained in the same package, which is described by the official documentation:

Pointer represents a pointer to an arbitrary type. There are four special operations available for type Pointer that are not available for other types.

The operations:

1) A pointer value of any type can be converted to a Pointer.

2) A Pointer can be converted to a pointer value of any type.

3) A uintptr can be converted to a Pointer.

4) A Pointer can be converted to a uintptr.

Note the distinction between pointer(generic type) and Pointer(unsafe.Pointer)

A pointer of type unsafe.Pointer is capable of overriding Go's type system. A uintptr can be converted to an unsafe.Pointer type and vice versa. A pointer of any type can be converted into an unsafe.Pointer.

I've recently learned that Go does not support pointer arithmetic (more on this later). Coming from C which does allow such functionality, I do think that this is limiting, but also considerate. Here's two reasons why Go's unsafe.Pointer pointer type is beneficial:

  • It allows programmers to have more control over data.

  • Limits this control to people who are aware of its capabilities and consequences. You can only shoot yourself in the foot if you want to!

So, making these four operations unavailable to pointers by default seems to be more of a safeguard.

Usage

Simply convert to and from each type as needed.

// unsafe.Pointer to a pointer to an integer.  
(* int)(unsafePtr)

// Pointer to an integer to an unsafe.Pointer
unsafe.Pointer(intPtr)

// unsafe.Pointer to a uintptr
uintptr(unsafePtr)

// uintptr to unsafe.Pointer
unsafe.Pointer(unsignedIntVar)

// uintptr to a pointer to an integer
(* int)(unsafe.Pointer(unsignedIntVar))

// pointer to an integer to a uintptr
uintptr(unsafe.Pointer(intPtr))  

Example(s)

Will add more as they come up! Feel free to suggest some.

Pointer Arithmetic

Taking advantage of the unsafe.Pointer type allows us to actually do some pointer arithmetic, albeit in an ugly way. I just wanted to demonstrate that it's possible.

The following code is a short example of how pointer arithmetic can be achieved. It also shows a portion of the operations that can be performed on unsafe.Pointer.

Note that line 16:

addressHolder := uintptr(unsafe.Pointer(intPtr)) + unsafe.Sizeof(intArray[0])  

the assignment of an address to a uintptr is illegal in Go 1.3 (see notes section), and is intentionally added to demonstrate this point.

basic_pointer_arithmetic.go|playground link

package main

import (  
    "fmt"
    "unsafe"
)

func main() {  
    intArray := [...]int{1, 2}

    fmt.Printf("\nintArray: %v\n", intArray)

    intPtr := &intArray[0]
    fmt.Printf("\nintPtr=%p, *intPtr=%d.\n", intPtr, *intPtr)

    addressHolder := uintptr(unsafe.Pointer(intPtr)) + unsafe.Sizeof(intArray[0])

    intPtr = (*int)(unsafe.Pointer(addressHolder))

    fmt.Printf("\nintPtr=%p, *intPtr=%d.\n\n", intPtr, *intPtr)
}

I kept the example code as short as I could for the sake of brevity. Refer to pointer_arithmetic.go | playground link for a more thorough and detailed explanation.

Output

intArray: [1 2]

intPtr=0x10328130, *intPtr=1.

intPtr=0x10328134, *intPtr=2.  

As you can see, intPtr originally pointed to the first element of intArray, and was later changed to point to the second one.

Code analysis

There is an array of two integers, 1 and 2. A pointer to an integer intPtr is declared and assigned the address of the first element.

Line 16:

addressHolder := uintptr(unsafe.Pointer(intPtr)) + unsafe.Sizeof(intArray[0])  

A uintptr variable addressHolder is declared and assigned the address that intPtr points to + an additional couple of bytes, the size of an element of the particular array. Incrementing the current address by the size in bytes of an element effectively moves the pointer to the next element. Note how intPtr was converted to an unsafe.Pointer, and then to an uintptr.

Line 18:

intPtr = (*int)(unsafe.Pointer(addressHolder))  

Finally, our integer pointer intPtr is assigned the value of addressHolder. Here, we demonstrate that unsafe.Pointer can be converted to a pointer of any type.

Notes

  • A recent change in the Garbage Collector of Go 1.3 concerns how unsafe.Pointer should be used. As the release notes states:

...Programs that use package unsafe to store integers in pointer-typed values are illegal and will crash if the runtime detects the behavior. Programs that use package unsafe to store pointers in integer-typed values are also illegal...

For additional details, refer to my previous post on the Garbage Collection Change in Go 1.3. To detect such illegal behavior, run go vet on the suspect package. Applying it to pointer_arithmetic.go results in:

    $ go vet lgwm
    src/lgwm/pointer_arithmetic.go:27: possible misuse of unsafe.Pointer
    exit status 1
  • A uintptr is an integer that is large enough to hold the bit pattern of any address. The size of a uintptr is 32 or 64 bits depending on your machine. Don't be fooled by its name. a uintptr is an integer data type, and while it can contain an address, Go 1.3's garbage collector change doesn't recommend that an address be stord in a uintptr.

Final Thoughts, Unrelated Comments

  • Apologies for the unusually late update!

  • I would appreciate any suggestions on upcoming content that people would like to see or haven't seen much of.

  • There are a couple of posts that are still brewing as drafts. To name a few, a detailed writeup on a simple webserver using the net/http package, pass by value vs pass by reference, integrating a database into a Go web application. Sounds like overkill, but these are topics that I either need to learn or think that many would benefit from.

  • I've completed 40/101 problems on my Go learning repo, Go 101. There will be some problems that are featured as future posts!

  • Working on adding code highlighting.