Dynamic array
An array is relatively straightforward to picture. It is a variable which contains multiple values. In C/C++, arrays have a fixed size. Now we know about memory management we can build our own data type and functions to build a dynamic array, where its size can grow and shrink based on its usage.
The Plan
Ok, let’s think about what we are planning on doing. We are going to create a dynamic array. This will need some data, which we can put within a struct, and then we will need some functions and procedures to help us work with this new type we have created. The following image shows sketches out the main aspects of the plan.
We will have a dynamic_array<T>
struct. This will use generics so that the array can contain any type the programmer who uses it wants (including ourselves). Within the struct we will need fields to keep track of the size
and the location of the data
.
To avoid resizing every time the user adds something, we can also have a capacity
that keeps track of how big the allocation is in memory. Our code will need to make sure that size is always less than or equal to capacity. If you try to add something when we are at capacity, then the size of the array can be changed. As a start, we can double the capacity of the array each time we fill it. This will also help avoid resizing the array frequently. We can create a resize
function to handle the necessary steps to manage memory for us.
Notice in the image, the example struct has a size of 3, but a capacity of 7. This means we can add 4 more values to this array before it will need to resize. When we add the 5th element, it will need to resize to be double its capacity allowing it to store up to 14 elements.
In terms of functionality, we can build the following functions and procedures. These functions should allow us to provide a convenient tool, and we can extend the features as we go. To avoid issues where the user copies dynamic_array
, we can make sure that we always work with a pointer to the dynamic_array
.
- A
new_dynamic_array
function that will initialise the data in the struct. As we want to have this on the heap, this will allocate the memory for thedynamic_array
and enough space for the initial capacity in the array’sdata
field. - Once we add the code to create the array, we should next create a
delete_dynamic_array
to free these allocations. This can free both thedata
and thedynamic_array
. We can set values in memory to avoid issues if there are dangling pointers. - To put data in the array we can create an
add
function. This will check if there is capacity, and resize if needed. We can make this return a boolean so that it can returnfalse
if it cannot get space to add the data. This will only happen if we are out of memory, so it won’t happen often. - A
resize
function can be told the new capacity, and change the array’sdata
field memory allocation. This can grow or shrink the size of the array. We can call it fromadd
, and it can be called by users of the dynamic array. - Once a value is added to the array, we can read it using
get
and update it usingset
. These can do the bounds checking, and make sure that they only get or set values that are allocated within the array. The get will need to accept adefault_value
that can be returned if we ask for something out of bounds. We cannot just return a fixed0
or""
as we do not know the type.
We can build this in a few steps.
- Generate and initialise the data structure
- Implement add and resize
- Build the get and set functions
But before we get started, there is one tool that can help us with this.
Getting Your Copilot
For this task we will be using the GitHub Copilot. Make sure you have followed the instructions from the page on Using an AI Copilot. This task is something the AI will smash out. There are lots of examples that it will have seen for how this is built, so you are likely to get most of this code with a few well coded prompts.