In this article, you will learn pointers in Mojo, their types, life-cycle, referencing, de-referencing etc., with real-world examples, visuals and easy code snippets for beginners.
Real-World Analogy of Pointers
Imagine you have a library with many books. Each book has a unique location on a shelf, but the library doesn’t always carry the book to you. Instead, they give you the shelf number where the book is located. This shelf number is like a pointer—rather than carrying the whole book (data), you are given the location (memory address) to access it when needed. Just like in the following scenario, each pointer is pointing to a different shelf on the rack.
In programming, pointers work similarly. Instead of copying large data like a book, a pointer stores the memory location where the data is. This allows efficient access to the data, just like how the shelf number lets you access a book without moving it around.
What are pointers?
Pointers are variables that points to another variable by storing their memory address. It allows indirect access to its value. Instead of holding data directly, they point to the location in memory where the data is stored.
Syntax
let myPointer: UnsafePointer<Int32>
Example Code
- Imports UnsafePointer: Brings in UnsafePointer from the memory module to use in the code.
- Declare a pointer: myPointer is an UnsafePointer of type Int32, and it allocates memory for one integer.
- Initialise value: Sets the value at the allocated memory to 42.
- Print value: Outputs the value stored at the memory location using myPointer[].val.
- Clean up: Destroys the value and then frees the allocated memory to prevent memory leaks.
var myPointer: UnsafePointer[Int32] = UnsafePointer[Int32].alloc(1) myPointer.init_pointee(42) print(myPointer[].val) myPointer.destroy_pointee() myPointer.free()
Output
42
Life Cycle of a Pointer in Mojo
The life cycle of pointer shows that a pointer in programming could be in any one of these 5 states which are as follows:
Uninitialized
A pointer is declared but not yet assigned any memory address or value.
Syntax
var ptr: UnsafePointer[Int]
Example Code
var value: Int = 42
: Declares an integer variablevalue
and initializes it with42
.var ptr: UnsafePointer[Int] = UnsafePointer[Int].
address_of(value)
: Initializesptr
to hold the address ofvalue
.- The
address_of
method assigns the memory address ofvalue
toptr
.print("Address stored in ptr: \(ptr)")
: Prints the memory address thatptr
holds. This shows the address wherevalue
is stored. print("Value pointed to by ptr: \(ptr[])")
: Dereferencesptr
to access the value it points to and prints42
.
var value: Int = 42 // Declare an integer variable var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value) // Initialize the pointer to point to the address of 'value' // Print the address stored in the pointer and the value it points to print("Address stored in ptr: \(ptr)") print("Value pointed to by ptr: \(ptr[])")
Output
Address stored in ptr: <memory address> Value pointed to by ptr: 42
Null
A null pointer has an address of 0, which means it doesn’t point to any valid memory location.
Syntax
ptr = UnsafePointer[Int]() // Points to address 0
Example Code
var ptr: UnsafePointer[Int] = UnsafePointer[Int]()
: Initializesptr
as a null pointer, which means it points to address0
and does not reference any valid memory.if ptr == UnsafePointer[Int]()
checks ifptr
is a null pointer.print("ptr is a null pointer and does not point to any valid address.")
: Outputs a message stating thatptr
is a null pointer.- If
ptr
were not null, theelse
block would print the memory address and the value it points to. However, since it is null, this part will not run.
var ptr: UnsafePointer[Int] = UnsafePointer[Int]() // Initializes ptr to a null pointer, pointing to address 0 // Check if the pointer is null and print an appropriate message if ptr == UnsafePointer[Int]() { print("ptr is a null pointer and does not point to any valid address.") } else { print("Address stored in ptr: \(ptr)") print("Value pointed to by ptr: \(ptr[])") }
Output
ptr is a null pointer and does not point to any valid address.
Pointing to Allocated, Uninitialized Memory
The alloc() method allocates a block of memory but doesn’t initialize it
Syntax
ptr = UnsafePointer[Int].alloc(1) // Allocates space for one Int
Example Code
var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1)
: Allocates memory for oneInt
and assigns the address toptr
.ptr.init_pointee(42)
: Initializes the allocated memory with the value42
.print("Address stored in ptr: \(ptr)")
: Outputs the memory address stored inptr
.print("Value at allocated memory: \(ptr[])")
: Dereferencesptr
to access and print the value42
.ptr.destroy_pointee()
: Cleans up the memory by destroying the value stored atptr
.ptr.free()
: Frees the allocated memory to prevent memory leaks.
var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1) // Allocates space for one Int // Initialize the allocated memory with a value ptr.init_pointee(42) // Print the address stored in the pointer print("Address stored in ptr: \(ptr)") // Print the value at the allocated memory print("Value at allocated memory: \(ptr[])") // Clean up the allocated memory ptr.destroy_pointee() ptr.free()
Output
Address stored in ptr: <memory_address> Value at allocated memory: 42
Pointing to Initialize Memory
After allocating memory, you can initialize it by copying or moving a value, or by pointing to an existing value.
Syntax
ptr.init_pointee_copy(value) // Copies value into memory ptr.init_pointee_move(value^) // Moves value into memory ptr = UnsafePointer[Int].address_of(value) // Points to existing value
Once initialized, you can read or modify the value
oldValue = ptr[] // Reads the value ptr[] = newValue // Modifies the value
Example Code
var value: Int = 50
: Declares and initializesvalue
with50
.var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1)
: Allocates memory for oneInt
and assigns the address toptr
.ptr.init_pointee_copy(value)
: Copiesvalue
(which is50
) into the allocated memory.var valueToMove: Int = 75
: Declares another integervalueToMove
initialized with75
.ptr.init_pointee_move(valueToMove^)
: MovesvalueToMove
into the allocated memory using a “move” operation.ptr = UnsafePointer[Int].address_of(value)
: Pointsptr
to the address ofvalue
.
var value: Int = 50 var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1) // Allocates space for one Int // Copying the value into allocated memory ptr.init_pointee_copy(value) print("Value after init_pointee_copy: \(ptr[])") // Moving the value into allocated memory (requires a copy of the original value) var valueToMove: Int = 75 ptr.init_pointee_move(valueToMove^) print("Value after init_pointee_move: \(ptr[])") // Pointing to an existing variable's address ptr = UnsafePointer[Int].address_of(value) print("Value at address_of(value): \(ptr[])")
Output
Value after init_pointee_copy: 50 Value after init_pointee_move: 75 Value at address_of(value): 50
Dangling
After freeing the allocated memory using free(), the pointer becomes dangling. It still holds the old address, but the memory is no longer valid
Syntax
ptr.free() // Frees the allocated memory
Example Code
var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1)
: Allocates memory for oneInt
and assigns the address toptr
.ptr.init_pointee(100)
: Initializes the allocated memory with the value100
.ptr.free()
: Frees the allocated memory, making it available for other uses in the program.print("Memory has been freed.")
: Outputs a message indicating that the memory has been freed.
var ptr: UnsafePointer[Int] = UnsafePointer[Int].alloc(1) // Allocates space for one Int ptr.init_pointee(100) // Initializes the memory with the value 100 print("Value before freeing: \(ptr[])") ptr.free() // Frees the allocated memory print("Memory has been freed.")
Output
Value before freeing: 100 Memory has been freed.
Types of Pointers in Mojo
There are two main types of pointers that mojo offers, they are as follows
- Unsafe Pointers
- Safe Pointers
Unsafe Pointers
Unsafe pointers in Mojo are used for low-level memory management. They allow you to manually work with memory addresses and perform operations directly on them. This gives you more control over memory, but also requires careful handling to avoid errors.
Syntax
var pointer: UnsafePointer[Type]
Example Code
- var value: Int = 10: Declares an integer variable value and initializes it with 10.
- var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value): Declares a pointer ptr and stores the memory address of value in it using address_of.
- print(ptr[]): Dereferences ptr to access the value at the memory address it points to and prints the value, which is 10.
var value: Int = 10 var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value) print(ptr[])
Output
10
Referencing with Unsafe Pointers
Referencing involves obtaining the memory address of a variable. When you reference a variable in Mojo, you create a pointer that holds this address, allowing you to directly access or modify the variable’s value through the pointer.
Syntax
var number: Int = 10 var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(number)
Example Code
var value: Int = 42
: Declares anInt
variable namedvalue
and initializes it with42
.var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value)
: Creates anUnsafePointer
namedptr
that holds the address ofvalue
.ptr[] = 55
: Dereferences the pointerptr
to update the value ofvalue
to55
.print("Updated value: \(value)")
: Prints the updated value ofvalue
.
var value: Int = 42 var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value) ptr[] = 55 // Updating the value using the pointer print("Updated value: \(value)") // Print the updated value of 'value'
Output
Updated value: 55
Dereferencing with Unsafe Pointer
Dereferencing means using a pointer to access the value stored at the memory address it points to. This is how you read or modify the data associated with that address.
Syntax
In Mojo, dereferencing is done using the [] syntax:
var value = ptr[] // Reads the value stored at the memory address ptr[] = 20 // Modifies the value at the memory address
Example Code
var value: Int = 100
: Declares anInt
variable namedvalue
and initializes it with100
.var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value)
: Creates anUnsafePointer
namedptr
that holds the address ofvalue
.var dereferencedValue: Int = ptr[]
: Dereferences the pointerptr
to read and store the value ofvalue
, which is100
.print("Original value: \(dereferencedValue)")
: Prints the original value ofvalue
, which is100
.ptr[] = 200
: Uses the pointerptr
to update the value ofvalue
to200
.print("Updated value: \(value)")
: Prints the updated value ofvalue
, which is200
.
var value: Int = 100 var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value) var dereferencedValue: Int = ptr[] // Dereferencing to get the value print("Original value: \(dereferencedValue)") ptr[] = 200 // Updating the value using the pointer print("Updated value: \(value)")
Output
Original value: 100 Updated value: 200
Declaration with Unsafe Pointers
Declaring a pointer defines a variable that can hold the address of another variable or allocated memory. At this stage, the pointer does not yet point to any specific memory address, making it uninitialized.
Syntax
var ptr: UnsafePointer[Type]
Example Code
- var value: Int = 50: Declares an Int variable value and assigns it the number 50.
- var numberPointer: UnsafePointer[Int] = UnsafePointer[Int].address_of(value): Initializes numberPointer to point to the memory address of value.
- print(numberPointer[]): Dereferences numberPointer to access and print the value it points to, which is 50.
var value: Int = 50 var numberPointer: UnsafePointer[Int] = UnsafePointer[Int].address_of(value) print(numberPointer[])
Output
50
Initialization with Unsafe Pointer
Initialising a pointer assigns it a valid memory address. This can be done by either allocating memory or referencing the address of an existing variable.
Syntax
var pointerName: UnsafePointer[Type] = UnsafePointer[Type].address_of(variable)
Example Code
- var number: Int = 30: Declares an Int variable number and sets it to 30.
- var initializedPointer: UnsafePointer[Int] = UnsafePointer[Int].address_of(number): Initializes initializedPointer to point to the memory address of number.
- print(initializedPointer[]): Dereferences initializedPointer to print the value 30.
var number: Int = 30 var initializedPointer: UnsafePointer[Int] = UnsafePointer[Int].address_of(number) print(initializedPointer[])
Output
30
Safe Pointers in Mojo
Safe pointers in Mojo manage memory more securely compared to unsafe pointers. They automatically handle memory management and ensure that pointers don’t reference invalid or deallocated memory, reducing the risk of common errors.
Syntax
var pointer: Pointer[Type]
Explanation
- var value: Int = 20: Declares an integer variable value and initializes it with 20.
- var ptr: Pointer[Int] = Pointer[Int].init(value): Creates a safe pointer ptr that references the value variable using the init method.
- print(ptr[]): Dereferences ptr to access and print the value at the memory address, which is 20.
var value: Int = 20 var ptr: Pointer[Int] = Pointer[Int].init(value) print(ptr[])
Output
20
Referencing in Safe Pointers
Referencing involves obtaining the memory address of a variable. In Mojo, when you reference a variable using a safe pointer, the memory management is handled securely, reducing risks.
Syntax
var number: Int = 10 var ptr: Pointer[Int] = Pointer[Int].init(number)
Example Code
var value: Int = 42
: Initializes the integer variablevalue
with 42.var ptr: Pointer[Int] = Pointer[Int].init(value)
: Creates a pointerptr
that points tovalue
.ptr[] = 55
: Dereferences the pointer and assigns 55 to the memory locationptr
is pointing to, effectively updating the value ofvalue
to 55.print("Updated value: \(value)")
: This prints the updated value ofvalue
, which will now be 55.print("Pointer address: \(ptr)")
: Prints the address of the pointerptr
(the memory address ofvalue
).
var value: Int = 42 var ptr: Pointer[Int] = Pointer[Int].init(value) ptr[] = 55 # Updating the value using the pointer // Print the updated value print("Updated value: \(value)")
Output
Updated value: 55
Dereferencing in Safe Pointers
Dereferencing means using a pointer to access the value stored at the memory address it points to. Safe pointers ensure that accessing the data doesn’t result in unsafe behaviour.
Syntax
var value = ptr[] ptr[] = 20
Example Code
var value: Int = 100
: Initializes the variablevalue
with 100.var ptr: Pointer[Int] = Pointer[Int].init(value)
: Creates a pointerptr
that points tovalue
.var dereferencedValue: Int = ptr[]
: Dereferences the pointerptr
and assigns the value it points to (100) todereferencedValue
.ptr[] = 200
: Updates the value at the memory location pointed to byptr
(which isvalue
) to 200.
var value: Int = 100 var ptr: Pointer[Int] = Pointer[Int].init(value) var dereferencedValue: Int = ptr[] // Print the dereferenced value print("Dereferenced value: \(dereferencedValue)") ptr[] = 200 # Updating the value using the pointer // Print the updated value print("Updated value: \(value)")
Output
Dereferenced value: 100 Updated value: 200
Declaration in Safe pointers
Declaring a safe pointer defines a variable that can hold the address of another variable. At this stage, the pointer does not yet point to any specific memory address
Syntax
var ptr: Pointer[Type]
Example Code
- var value: Int = 50: Declares an Int variable value and assigns it the number 50.
- var numberPointer: Pointer[Int] = Pointer[Int].init(value): Initializes numberPointer to point to the memory address of value.
- print(numberPointer[]): Dereferences numberPointer to access and print the value it points to, which is 50.
var value: Int = 50 var numberPointer: Pointer[Int] = Pointer[Int].init(value) print(numberPointer[])
Output
50
Initialization in Safe Pointer
Initializing a safe pointer assigns it a valid memory address. This is done by referencing the address of an existing variable
Syntax
var pointerName: Pointer[Type] = Pointer[Type].init(variable)
Example Code
- var number: Int = 30: Declares an Int variable number and sets it to 30.
- var initializedPointer: Pointer[Int] = Pointer[Int].init(number): Initializes initializedPointer to point to the memory address of number.
- print(initializedPointer[]): Dereferences initializedPointer to print the value 30.
var number: Int = 30 var initializedPointer: Pointer[Int] = Pointer[Int].init(number) print(initializedPointer[])
Output
30
Summary for Safe & Unsafe
Operations | Safe Pointer | Unsafe Pointer |
Declaration | var ptr: Pointer[Type] | var ptr: UnsafePointer[Type] |
Initialization | var ptr: Pointer[Type] =Pointer[Type].init(variable) | var ptr: UnsafePointer[Type] = UnsafePointer[Type].address_of(variable) |
Referencing | Pointer[Type].init(variable) | UnsafePointer[Type].address_of(variable) |
Dereferencing | ptr[] | ptr[] |
Pointer to Pointer
A pointer to pointer is a concept where a pointer holds the address of another pointer. This allows multi-level referencing, which means accessing or manipulating data through multiple levels of indirection.
Syntax
var ptrToPtr: UnsafePointer[UnsafePointer[Int]]
Example Code
- First var value: Int = 10: Declares an integer value and initializes it to 10.
- then var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value): Creates a pointer ptr that holds the address of value.
- Last but not least var ptrToPtr: UnsafePointer[UnsafePointer[Int]] = UnsafePointer[UnsafePointer[Int]].address_of(ptr): Declares ptrToPtr, which is a pointer to ptr. It holds the address of the ptr variable.
- var accessedValue: Int = ptrToPtr[][]: Uses double dereferencing (ptrToPtr[][]) to access the original value (10) through both levels of pointers.
var value: Int = 10 var ptr: UnsafePointer[Int] = UnsafePointer[Int].address_of(value) var ptrToPtr: UnsafePointer[UnsafePointer[Int]] = UnsafePointer[UnsafePointer[Int]].address_of(ptr) // Accessing the value through ptrToPtr var accessedValue: Int = ptrToPtr[][] print(accessedValue) // Added print statement to display the value
Output
10
Conclusion
In this article you learned what are pointers, the different types of pointers that mojo offers, along with the differences in those types as well as how you can declare, initialize, reference and dereference those pointers and how you can point a pointer to another pointer. To get a better idea of the syntax and background of this emerging language Mojo by Modular, follow our Mojo tutorials written by Syntax Scenarios.