struct Pointer(T)

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

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

Defined in:

pointer.cr
primitives.cr

Constructors

Class Method Summary

Instance Method Summary

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 : 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! 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, root : String)
from_json(string_or_io)
from_json
, from_yaml(string_or_io : String | IO) from_yaml

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

[View source]
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

[View source]
def self.new #

[View source]

Class Method Detail

def self.malloc(size : Int, value : 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

[View source]
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. Otherwise it uses GC.malloc_atomic. Primitive types are expected to not contain pointers, except Void. Proc and Pointer are expected to contain pointers. For unions, structs and collection types (tuples, static array) it depends on the contained types. All other types, including classes are expected to contain inner address pointers.

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


[View source]
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

[View source]
def self.malloc(size : Int, & : Int32 -> 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

[View source]
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

[View source]

Instance Method Detail

def +(offset : Int64) : self #

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_u64
ptr2.address # => 1238

[View source]
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

[View source]
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

[View source]
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

[View source]
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.


[View source]
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

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

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

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

ptr2 = ptr + 1
ptr2.value # => 42

[View source]
def address : UInt64 #

Returns the address of this pointer.

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

[View source]
def appender : Pointer::Appender #

Returns a Pointer::Appender for this pointer.


[View source]
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]

[View source]
def clone #

[View source]
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 }  # [1, 2, 3, 4]
ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14]

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

[View source]
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 }  # [1, 2, 3, 4]
ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14]

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

[View source]
def hash(hasher) #

Returns the address of this pointer.

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

[View source]
def initialize #

[View source]
def map!(count : Int, & : T -> T) #

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

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

[View source]
def map_with_index!(count : Int, offset = 0, &) #

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

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


[View source]
def memcmp(other : Pointer(T), count : Int) : Int32 #

Compares count elements from this pointer and other, byte by byte.

Returns 0 if both pointers point to the same sequence of count bytes. Otherwise returns the difference between the first two differing bytes (treated as UInt8).

ptr1 = Pointer.malloc(4) { |i| i + 1 }  # [1, 2, 3, 4]
ptr2 = Pointer.malloc(4) { |i| i + 11 } # [11, 12, 13, 14]

ptr1.memcmp(ptr2, 4) # => -10
ptr2.memcmp(ptr1, 4) # => 10
ptr1.memcmp(ptr1, 4) # => 0

[View source]
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 -> [1, 2, 3, 4]
ptr2 = ptr1 + 1                        #             ^--------- ptr2

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

ptr1[0] # => 1
ptr1[1] # => 1
ptr1[2] # => 2
ptr1[3] # => 3

[View source]
def move_to(target : Pointer, count : Int) #

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

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

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

ptr1[0] # => 1
ptr1[1] # => 1
ptr1[2] # => 2
ptr1[3] # => 3

[View source]
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

[View source]
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]

[View source]
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 } # [1, 2, 3, 4]
ptr = ptr.realloc(8)
ptr # [1, 2, 3, 4, 0, 0, 0, 0]

[View source]
def shuffle!(count : Int, random = Random::DEFAULT) #

Shuffles count consecutive values pointed by this pointer.

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

[View source]
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

[View source]
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"

[View source]
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)

[View source]
def value : T #

Gets the value pointed by this pointer.

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

[View source]
def value=(value : T) #

Sets the value pointed by this pointer.

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

[View source]