struct

Pointer(T)

Inherits Comparable / Value / Object

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.

Constructors

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
Source
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
Source

Class methods

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
Source
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.

Source
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
Source
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
Source
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
Source

Instance methods

+(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
Source
+(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
Source
-(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
Source
-(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
Source
<=>(other : self)

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

Source
[](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
Source
[]=(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]
Source
address

Returns the address of this pointer.

ptr = Pointer(Int32).new(1234)
ptr.address # => 1234
Source
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
Source
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
Source
appender

Returns a Pointer::Appender for this pointer.

Source
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]
Source
clone
Source
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]
Source
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]
Source
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]
Source
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]
Source
hash(hasher)

Returns the address of this pointer.

ptr = Pointer(Int32).new(1234)
ptr.hash # => 1234
Source
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]
Source
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.

Source
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
Source
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]
Source
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]
Source
null?

Returns true if this pointer's address is zero.

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

b = Pointer(Int32).new(0)
b.null? # => true
Source
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]
Source
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.

Source
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]
Source
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
Source
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"
Source
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)
Source
value

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)))
Source
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)))
Source

Nested types