Pointer(T) - Crystal 1.20.1

# struct Pointer(T)

- Pointer(T)
- Value
- Object

##
Overview

A typed pointer to some memory.

This is the only unsafe type in Crystal. If you are using a pointer, you are writing unsafe code because a pointer doesn't know where it's pointing to nor how much memory starting from it is valid. However, pointers make it possible to interface with C and to implement efficient data structures. For example, both `Array` and `Hash` are implemented using pointers.

You can obtain pointers in four ways: `#new`, `#malloc`, `pointerof`, or by calling a C function that returns a pointer.

`pointerof(x)`, where x is a variable or an instance variable, returns a pointer to that variable:

    x = 1
    ptr = pointerof(x)
    ptr.value = 2
    x # => 2

Use `#value` to dereference the pointer.

Note that a pointer is falsey if it's null (if its address is zero).

When calling a C function that expects a pointer you can also pass `nil` instead of using `Pointer.null` to construct a null pointer.

For a safe alternative, see `Slice`, which is a pointer with a size and with bounds checking.

##
Included Modules

- Comparable(Pointer(T))

##
Defined in:

pointer.cr
primitives.cr

##
Constructors

- .new(address : UInt64)

  Returns a pointer that points to the given memory address.

- .new(address : Int)

  Returns a pointer that points to the given memory address.

  DEPRECATED Call `.new(UInt64)` directly instead

##
Class Method Summary

- .malloc(size : Int, value : T) : Pointer(T)

  Allocates `size * sizeof(T)` bytes from the system's heap initialized to value and returns a pointer to the first byte from that memory.

- .malloc(size : UInt64)

  Allocates `size * sizeof(T)` bytes from the system's heap initialized to zero and returns a pointer to the first byte from that memory.

- .malloc(size : Int = 1)

  Allocates `size * sizeof(T)` bytes from the system's heap initialized to zero and returns a pointer to the first byte from that memory.

- .malloc(size : Int, & : Int32 -> T) : Pointer(T)

  Allocates `size * sizeof(T)` bytes from the system's heap initialized to the value returned by the block (which is invoked once with each index in the range `0...size`) and returns a pointer to the first byte from that memory.

- .null

  Returns a pointer whose memory address is zero.

##
Instance Method Summary

- #+(offset : Int64) : self

  Returns a new pointer whose address is this pointer's address incremented by `offset * sizeof(T)`.

- #+(other : Int)

  Returns a new pointer whose address is this pointer's address incremented by `other * sizeof(T)`.

- #-(other : self) : Int64

  Returns how many T elements are there between this pointer and other.

- #-(other : Int)

  Returns a new pointer whose address is this pointer's address decremented by `other * sizeof(T)`.

- #<=>(other : self)

  Returns `-1`, `0` or `1` depending on whether this pointer's address is less, equal or greater than other's address, respectively.

- #[](offset)

  Gets the value pointed at this pointer's address plus `offset * sizeof(T)`.

- #[]=(offset, value : T)

  Sets the value pointed at this pointer's address plus `offset * sizeof(T)`.

- #address : UInt64

  Returns the address of this pointer.

- #align_down(boundary : UInt64) : Pointer(T)

  Returns a pointer with the address of this pointer aligned downwards to the next given byte boundary.

- #align_up(boundary : UInt64) : Pointer(T)

  Returns a pointer with the address of this pointer aligned upwards to the next given byte boundary.

- #appender : Pointer::Appender

  Returns a `Pointer::Appender` for this pointer.

- #clear(count = 1)

  Clears (sets to "zero" bytes) a number of values pointed by this pointer.

- #clone
- #copy_from(source : Pointer(T), count : Int)

  Copies count elements from source into `self`.

- #copy_to(target : Pointer, count : Int)

  Copies count elements from `self` into target.

- #fill(count : Int, value : T) : self

  Replaces count elements in `self` with value.

- #fill(count : Int, *, offset : Int = 0, &) : self

  Yields count indices starting from `self` to the given block and then assigns the block's output value in that position.

- #hash(hasher)

  Returns the address of this pointer.

- #map!(count : Int, & : T -> T) : self

  Sets count consecutive values pointed by this pointer to the values returned by the block.

- #map_with_index!(count : Int, offset = 0, &) : self

  Like `#map!`, but yields 2 arguments, the element and its index

- #memcmp(other : Pointer(T), count : Int) : Int32

  Compares count elements from this pointer and other, lexicographically.

- #move_from(source : Pointer(T), count : Int)

  Copies count elements from source into `self`.

- #move_to(target : Pointer, count : Int)

  Copies count elements from `self` into target.

- #null? : Bool

  Returns `true` if this pointer's address is zero.

- #realloc(size : UInt64) : self

  Tries to change the size of the allocation pointed to by this pointer to size, and returns that pointer.

- #realloc(size : Int)

  Tries to change the size of the allocation pointed to by this pointer to size, and returns that pointer.

- #shuffle!(count : Int, random : Random | Nil = nil)

  Shuffles count consecutive values pointed by this pointer.

- #swap(i, j)

  Swaps the contents pointed at the offsets i and j.

- #to_s(io : IO) : Nil

  Appends a string representation of this pointer to the given `IO`, including its type and address in hexadecimal.

- #to_slice(size) : Slice(T)

  Returns a `Slice` that points to this pointer and is bounded by the given size.

- #value : T

  Gets the value pointed by this pointer.

- #value=(value : T)

  Sets the value pointed by this pointer.

### Instance methods inherited from module `Comparable(Pointer(T))`

<(other : T(T)) : Bool <, <=(other : T(T)) <=, <=>(other : T(T)) <=>, ==(other : T(T)) ==, >(other : T(T)) : Bool >, >=(other : T(T)) >=, clamp(min, max)
clamp(range : Range) clamp

### Instance methods inherited from struct `Value`

==(other : Log::Metadata::Value)
==(other : JSON::Any)
==(other : YAML::Any)
==(other) ==, dup dup

### Instance methods inherited from class `Object`

! : Bool !, !=(other) !=, !~(other) !~, ==(other) ==, ===(other : JSON::Any)
===(other : YAML::Any)
===(other) ===, =~(other) =~, as(type : Class) as, as?(type : Class) as?, class class, dup dup, hash(hasher)
hash hash, in?(collection : Object) : Bool
in?(*values : Object) : Bool in?, inspect(io : IO) : Nil
inspect : String inspect, is_a?(type : Class) : Bool is_a?, itself itself, nil? : Bool nil?, not_nil!(message)
not_nil! not_nil!, pretty_inspect(width = 79, newline = "\n", indent = 0) : String pretty_inspect, pretty_print(pp : PrettyPrint) : Nil pretty_print, responds_to?(name : Symbol) : Bool responds_to?, tap(&) tap, to_json(io : IO) : Nil
to_json : String to_json, to_pretty_json(indent : String = " ") : String
to_pretty_json(io : IO, indent : String = " ") : Nil to_pretty_json, to_s(io : IO) : Nil
to_s : String to_s, to_yaml(io : IO) : Nil
to_yaml : String to_yaml, try(&) try, unsafe_as(type : T.class) forall T unsafe_as

### Class methods inherited from class `Object`

from_json(string_or_io : String | IO, root : String)
from_json(string_or_io : String | IO) from_json, from_yaml(string_or_io : String | IO) from_yaml

### Macros inherited from class `Object`

class_getter(*names, &block) class_getter, class_getter!(*names) class_getter!, class_getter?(*names, &block) class_getter?, class_property(*names, &block) class_property, class_property!(*names) class_property!, class_property?(*names, &block) class_property?, class_setter(*names) class_setter, def_clone def_clone, def_equals(*fields) def_equals, def_equals_and_hash(*fields) def_equals_and_hash, def_hash(*fields) def_hash, delegate(*methods, to object) delegate, forward_missing_to(delegate) forward_missing_to, getter(*names, &block) getter, getter!(*names) getter!, getter?(*names, &block) getter?, property(*names, &block) property, property!(*names) property!, property?(*names, &block) property?, setter(*names) setter

##
Constructor Detail

def self.new(address : UInt64) #

Returns a pointer that points to the given memory address. This doesn't allocate memory.

    ptr = Pointer(Int32).new(5678_u64)
    ptr.address # => 5678

def self.new(address : Int) #

Returns a pointer that points to the given memory address. This doesn't allocate memory.

    ptr = Pointer(Int32).new(5678)
    ptr.address # => 5678

DEPRECATED Call `.new(UInt64)` directly instead

##
Class Method Detail

def self.malloc(size : Int, value : T) : Pointer(T) #

Allocates `size * sizeof(T)` bytes from the system's heap initialized to value and returns a pointer to the first byte from that memory. The memory is allocated by the `GC`, so when there are no pointers to this memory, it will be automatically freed.

    # An Int32 occupies 4 bytes, so here we are requesting 8 bytes
    # initialized to the number 42
    ptr = Pointer.malloc(2, 42)
    ptr[0] # => 42
    ptr[1] # => 42

def self.malloc(size : UInt64) #

Allocates `size * sizeof(T)` bytes from the system's heap initialized to zero and returns a pointer to the first byte from that memory. The memory is allocated by the `GC`, so when there are no pointers to this memory, it will be automatically freed.

    # Allocate memory for an Int32: 4 bytes
    ptr = Pointer(Int32).malloc(1_u64)
    ptr.value # => 0

    # Allocate memory for 10 Int32: 40 bytes
    ptr = Pointer(Int32).malloc(10_u64)
    ptr[0] # => 0
    # ...
    ptr[9] # => 0

The implementation uses `GC.malloc` if the compiler is aware that the allocated type contains inner address pointers. See `Crystal::Macros::TypeNode#has_inner_pointers?` for details.

To override this implicit behaviour, `GC.malloc` and `GC.malloc_atomic` can be used directly instead.

def self.malloc(size : Int = 1) #

Allocates `size * sizeof(T)` bytes from the system's heap initialized to zero and returns a pointer to the first byte from that memory. The memory is allocated by the `GC`, so when there are no pointers to this memory, it will be automatically freed.

    # Allocate memory for an Int32: 4 bytes
    ptr = Pointer(Int32).malloc
    ptr.value # => 0

    # Allocate memory for 10 Int32: 40 bytes
    ptr = Pointer(Int32).malloc(10)
    ptr[0] # => 0
    # ...
    ptr[9] # => 0

def self.malloc(size : Int, & : Int32 -> T) : Pointer(T) #

Allocates `size * sizeof(T)` bytes from the system's heap initialized to the value returned by the block (which is invoked once with each index in the range `0...size`) and returns a pointer to the first byte from that memory. The memory is allocated by the `GC`, so when there are no pointers to this memory, it will be automatically freed.

    # An Int32 occupies 4 bytes, so here we are requesting 16 bytes.
    # i is an index in the range 0 .. 3
    ptr = Pointer.malloc(4) { |i| i + 10 }
    ptr[0] # => 10
    ptr[1] # => 11
    ptr[2] # => 12
    ptr[3] # => 13

def self.null #

Returns a pointer whose memory address is zero. This doesn't allocate memory.

When calling a C function you can also pass `nil` instead of constructing a null pointer with this method.

    ptr = Pointer(Int32).null
    ptr.address # => 0

##
Instance Method Detail

def +(offset : Int64) : self #

Returns a new pointer whose address is this pointer's address incremented by `offset * sizeof(T)`.

    ptr = Pointer(Int32).new(1234)
    ptr.address # => 1234

    # An Int32 occupies four bytes
    ptr2 = ptr + 1_u64
    ptr2.address # => 1238

def +(other : Int) #

Returns a new pointer whose address is this pointer's address incremented by `other * sizeof(T)`.

    ptr = Pointer(Int32).new(1234)
    ptr.address # => 1234

    # An Int32 occupies four bytes
    ptr2 = ptr + 1
    ptr2.address # => 1238

def -(other : self) : Int64 #

Returns how many T elements are there between this pointer and other. That is, this is `(self.address - other.address) / sizeof(T)`.

    ptr1 = Pointer(Int32).malloc(4)
    ptr2 = ptr1 + 2
    ptr2 - ptr1 # => 2

def -(other : Int) #

Returns a new pointer whose address is this pointer's address decremented by `other * sizeof(T)`.

    ptr = Pointer(Int32).new(1234)
    ptr.address # => 1234

    # An Int32 occupies four bytes
    ptr2 = ptr - 1
    ptr2.address # => 1230

def <=>(other : self) #

Returns `-1`, `0` or `1` depending on whether this pointer's address is less, equal or greater than other's address, respectively.

def [](offset) #

Gets the value pointed at this pointer's address plus `offset * sizeof(T)`.

    ptr = Pointer.malloc(4) { |i| i + 10 }
    ptr[0] # => 10
    ptr[1] # => 11
    ptr[2] # => 12
    ptr[3] # => 13

def []=(offset, value : T) #

Sets the value pointed at this pointer's address plus `offset * sizeof(T)`.

    ptr = Pointer(Int32).malloc(4)
    ptr.to_slice(4) # => Slice[0, 0, 0, 0]
    ptr[1] = 42

    ptr2 = ptr + 1
    ptr2.value       # => 42
    ptr1.to_slice(4) # => Slice[0, 42, 0, 0]

def address : UInt64 #

Returns the address of this pointer.

    ptr = Pointer(Int32).new(1234)
    ptr.address # => 1234

def align_down(boundary : UInt64) : Pointer(T) #

Returns a pointer with the address of this pointer aligned downwards to the next given byte boundary.

INFO: This method requires the given parameter to be a power of 2

INFO: This method aligns on byte boundaries, not sizeof(T) boundaries

    ptr = Pointer(Void).new(0x30_u64)
    ptr.align_down(16) # => Pointer(Void)@0x30
    ptr.align_down(32) # => Pointer(Void)@0x20

def align_up(boundary : UInt64) : Pointer(T) #

Returns a pointer with the address of this pointer aligned upwards to the next given byte boundary.

INFO: This method requires the given parameter to be a power of 2

INFO: This method aligns on byte boundaries, not sizeof(T) boundaries

    ptr = Pointer(Void).new(0x30_u64)
    ptr.align_up(16) # => Pointer(Void)@0x30
    ptr.align_up(32) # => Pointer(Void)@0x40

def appender : Pointer::Appender #

Returns a `Pointer::Appender` for this pointer.

def clear(count = 1) #

Clears (sets to "zero" bytes) a number of values pointed by this pointer.

    ptr = Pointer.malloc(6) { |i| i + 10 } # [10, 11, 12, 13, 14, 15]
    ptr.clear(3)
    ptr.to_slice(6) # => Slice[0, 0, 0, 13, 14, 15]

def clone #
def copy_from(source : Pointer(T), count : Int) #

Copies count elements from source into `self`. If source and `self` overlap, behaviour is undefined. Use `#move_from` if they overlap (slower but always works).

    ptr1 = Pointer.malloc(4) { |i| i + 1 }
    ptr2 = Pointer.malloc(4) { |i| i + 11 }

    ptr1.to_slice(4) # => Slice[1, 2, 3, 4]
    ptr2.to_slice(4) # => Slice[11, 12, 13, 14]

    # ptr2 -> [11, 12, 13, 14]
    #          ^---^           <- copy this
    # ptr1 -> [1,  2,  3,  4]
    #          ^---^           <- here
    ptr1.copy_from(ptr2, 2)
    ptr1.to_slice(4) # => Slice[1, 12, 3, 4]

def copy_to(target : Pointer, count : Int) #

Copies count elements from `self` into target. If `self` and target overlap, behaviour is undefined. Use `#move_to` if they overlap (slower but always works).

    ptr1 = Pointer.malloc(4) { |i| i + 1 }
    ptr2 = Pointer.malloc(4) { |i| i + 11 }

    ptr1.to_slice(4) # => Slice[1, 2, 3, 4]
    ptr2.to_slice(4) # => Slice[11, 12, 13, 14]

    # ptr1 -> [1,  2,  3,  4]
    #          ^---^           <- copy this
    # ptr2 -> [11, 12, 13, 14]
    #          ^---^           <- here
    ptr1.copy_to(ptr2, 2)
    ptr2.to_slice(4) # => Slice[1, 2, 13, 14]

def fill(count : Int, value : T) : self #

Replaces count elements in `self` with value. Returns `self`.

    ptr = Pointer(Int32).malloc(5) { |i| i }
    ptr.to_slice(5) # => Slice[0, 1, 2, 3, 4]
    ptr.fill(3, 0)
    ptr.to_slice(5) # => Slice[0, 0, 0, 0, 4]

def fill(count : Int, *, offset : Int = 0, &) : self #

Yields count indices starting from `self` to the given block and then assigns the block's output value in that position. Returns `self`.

    ptr = Pointer(Int32).malloc(5) { |i| i }
    ptr.to_slice(5) # => Slice[0, 1, 2, 3, 4]

    (ptr + 1).fill(3) { |i| i * i }
    ptr.to_slice(5) # => Slice[0, 0, 1, 4, 4]

    (ptr + 1).fill(3, offset: 3) { |i| i * i }
    ptr.to_slice(5) # => Slice[0, 9, 16, 25, 4]

def hash(hasher) #

Returns the address of this pointer.

    ptr = Pointer(Int32).new(1234)
    ptr.hash # => 1234

def map!(count : Int, & : T -> T) : self #

Sets count consecutive values pointed by this pointer to the values returned by the block.

    ptr = Pointer.malloc(4) { |i| i + 1 }
    ptr.to_slice(4) # => Slice[1, 2, 3, 4]
    ptr.map!(4) { |value| value * 2 }
    ptr.to_slice(4) # => Slice[2, 4, 6, 8]

def map_with_index!(count : Int, offset = 0, &) : self #

Like `#map!`, but yields 2 arguments, the element and its index

Accepts an optional offset parameter, which tells it to start counting from there.

def memcmp(other : Pointer(T), count : Int) : Int32 #

Compares count elements from this pointer and other, lexicographically.

Returns 0 if both pointers point to the same sequence of count bytes. Otherwise, if the first two differing bytes (treated as UInt8) from `self` and other are `x` and `y` respectively, returns a negative value if `x < y`, or a positive value if `x > y`.

    ptr1 = Pointer.malloc(4) { |i| i + 1 }
    ptr2 = Pointer.malloc(4) { |i| i + 11 }

    ptr1.to_slice(4) # => Slice[1, 2, 3, 4]
    ptr2.to_slice(4) # => Slice[11, 12, 13, 14]

    ptr1.memcmp(ptr2, 4) < 0  # => true
    ptr2.memcmp(ptr1, 4) > 0  # => true
    ptr1.memcmp(ptr1, 4) == 0 # => true

def move_from(source : Pointer(T), count : Int) #

Copies count elements from source into `self`. source and `self` may overlap; the copy is always done in a non-destructive manner.

    ptr1 = Pointer.malloc(4) { |i| i + 1 }
    ptr1.to_slice(4) # => Slice[1, 2, 3, 4]
    #                              ^-- ptr2
    ptr2 = ptr1 + 1

    # [1, 2, 3, 4]
    #  ^-----^       <- copy this
    #     ^------^   <- here
    ptr2.move_from(ptr1, 3)

    ptr1.to_slice(4) # => Slice[1, 1, 2, 3]

def move_to(target : Pointer, count : Int) #

Copies count elements from `self` into target. target and `self` may overlap; the copy is always done in a non-destructive manner.

    ptr1 = Pointer.malloc(4) { |i| i + 1 }
    ptr1.to_slice(4) # => Slice[1, 2, 3, 4]
    #                              ^-- ptr2
    ptr2 = ptr1 + 1

    # [1, 2, 3, 4]
    #  ^-----^       <- copy this
    #     ^------^   <- here
    ptr1.move_to(ptr2, 3)

    ptr1.to_slice(4) # => Slice[1, 1, 2, 3]

def null? : Bool #

Returns `true` if this pointer's address is zero.

    a = 1
    pointerof(a).null? # => false

    b = Pointer(Int32).new(0)
    b.null? # => true

def realloc(size : UInt64) : self #

Tries to change the size of the allocation pointed to by this pointer to size, and returns that pointer.

Since the space after the end of the block may be in use, realloc may find it necessary to copy the block to a new address where more free space is available. The value of realloc is the new address of the block. If the block needs to be moved, realloc copies the old contents.

Remember to always assign the value of realloc.

    ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4]
    ptr = ptr.realloc(8_u8)
    ptr # [1, 2, 3, 4, 0, 0, 0, 0]

def realloc(size : Int) #

Tries to change the size of the allocation pointed to by this pointer to size, and returns that pointer.

Since the space after the end of the block may be in use, realloc may find it necessary to copy the block to a new address where more free space is available. The value of realloc is the new address of the block. If the block needs to be moved, realloc copies the old contents.

Remember to always assign the value of realloc.

    ptr = Pointer.malloc(4) { |i| i + 1 }
    ptr.to_slice(4) # => Slice[1, 2, 3, 4]

    ptr = ptr.realloc(8)
    ptr.to_slice(8) # => Slice[1, 2, 3, 4, 0, 0, 0, 0]

WARNING Memory allocated using `GC.malloc` or `GC.malloc_atomic` must be reallocated using `GC.realloc` instead.

def shuffle!(count : Int, random : Random | Nil = nil) #

Shuffles count consecutive values pointed by this pointer.

    ptr = Pointer.malloc(4) { |i| i + 1 }
    ptr.to_slice(4) # => Slice[1, 2, 3, 4]
    ptr.shuffle!(4)
    ptr.to_slice(4) # => Slice[3, 4, 1, 2]

def swap(i, j) #

Swaps the contents pointed at the offsets i and j.

    ptr = Pointer.malloc(4) { |i| i + 1 }
    ptr[2] # => 3
    ptr[3] # => 4
    ptr.swap(2, 3)
    ptr[2] # => 4
    ptr[3] # => 3

def to_s(io : IO) : Nil #

Appends a string representation of this pointer to the given `IO`, including its type and address in hexadecimal.

    ptr1 = Pointer(Int32).new(1234)
    ptr1.to_s # => "Pointer(Int32)@0x4d2"

    ptr2 = Pointer(Int32).new(0)
    ptr2.to_s # => "Pointer(Int32).null"

def to_slice(size) : Slice(T) #

Returns a `Slice` that points to this pointer and is bounded by the given size.

    ptr = Pointer.malloc(6) { |i| i + 10 } # [10, 11, 12, 13, 14, 15]
    slice = ptr.to_slice(4)                # => Slice[10, 11, 12, 13]
    slice.class                            # => Slice(Int32)

def value : T #

Gets the value pointed by this pointer.

    ptr = Pointer(Int32).malloc(4)
    ptr.value = 42
    ptr.value # => 42

WARNING The pointer must be appropriately aligned, i.e. `#address` must be a multiple of `alignof(T)`. It is undefined behavior to load from a misaligned pointer. Such reads should instead be done via a cast to `Pointer(UInt8)`, which is guaranteed to have byte alignment:

    # raises SIGSEGV on X86 if `ptr` is misaligned
    x = ptr.as(UInt128*).value

    # okay, `ptr` can have any alignment
    x = uninitialized UInt128
    ptr.as(UInt8*).copy_to(pointerof(x).as(UInt8*), sizeof(typeof(x)))

def value=(value : T) #

Sets the value pointed by this pointer.

    ptr = Pointer(Int32).malloc(4)
    ptr.value = 42
    ptr.value # => 42

WARNING The pointer must be appropriately aligned, i.e. `#address` must be a multiple of `alignof(T)`. It is undefined behavior to store to a misaligned pointer. Such writes should instead be done via a cast to `Pointer(UInt8)`, which is guaranteed to have byte alignment:

    # raises SIGSEGV on X86 if `ptr` is misaligned
    x = 123_u128
    ptr.as(UInt128*).value = x

    # okay, `ptr` can have any alignment
    ptr.as(UInt8*).copy_from(pointerof(x).as(UInt8*), sizeof(typeof(x)))