In this article we will show how to develop a Dynamic Array in C and also talk about the advantages and disadvantages of each strategy of memory initialization for arrays.
Prerequisites:
Basic knowledge of programming (logic, etc);
C syntax, allocating variables;
Memory management in C: pointers, malloc, calloc, realloc, free;
What is a Dynamic Array?
An array that can grow or shrink in size during runtime.
Unlike static arrays, which have a fixed size, dynamic arrays automatically resize as elements are added or removed.
Sizing an Array appropriately
Is it a good idea to start an array with a small size and then realloc it? It depends.
If the current memory block can't be extended contiguously, realloc will allocate a new block and copy all n items. Which results in O(n) operation and can cause an overhead.
When prioritizing performance we will prefer to initialize the array with a large size.
When prioritizing memory saving we will prefer to initialize the array with a small size.
Initializing with a Small Size:
Ideal when the expected number of items is unknown or varies significantly.
This approach offers memory efficiency by only allocating the necessary memory for the array, avoiding waste. However, it can incur performance overhead due to frequent reallocations and potential fragmentation.
Initializing with a Large Size:
This is especially useful when the array size is predictable.
Using a larger initial size for a dynamic array can be more efficient because it reduces the need for frequent reallocations and avoids the overhead of copying data multiple times.
However, the trade-off is that it might waste memory if the array doesn't grow as much as expected. Overall, this strategy is beneficial when performance is a priority, and the array's growth pattern is relatively known.
How to Develop a Dynamic Array in C
In this code, we define an int pointer using malloc. While calloc could be used to initialize the array with zeros for memory safety, it's not necessary for this example. We'll discuss memory safety in another article.
To benefit from dynamic memory allocation, we will start the array with a small capacity of 2 integers, and will grow when needed using realloc, we will double the capacity every time we reach the limit.
#include<stdio.h>
#include<stdlib.h>voiddynamicArrayExample(intstartCapacity);voidpushToDynamicArray(int**array,intvalue,int*currentLength,int*currentCapacity);voidprintArrayItems(int*array,intarrayLength,chararrayName[]);intmain(){intinitCapacity=2;dynamicArrayExample(initCapacity);return0;}// EXAMPLE: Doubling the capacity every time we reach the limitvoiddynamicArrayExample(intstartCapacity){int*numbers=(int*)malloc(sizeof(int)*startCapacity);if(numbers==NULL){printf("malloc failed.\n");exit(1);}intcurrentArrayCapacity=startCapacity;intcurrentArrayLength=startCapacity;numbers[0]=10;numbers[1]=11;printArrayItems(numbers,currentArrayLength,"numbers");// numbers: 10, 11.// numbers[0] = 10; -> this could lead to an unexpected behavior writing on memory that is not "held" by the pointer// adding the 3rd item | capacity goes from 2 to 4pushToDynamicArray(&numbers,12,¤tArrayLength,¤tArrayCapacity);printArrayItems(numbers,currentArrayLength,"numbers");// numbers: 10, 11, 12.// adding the 4th item | capacity remains 4pushToDynamicArray(&numbers,13,¤tArrayLength,¤tArrayCapacity);// adding the 5th item | capacity goes from 4 to 8pushToDynamicArray(&numbers,14,¤tArrayLength,¤tArrayCapacity);printArrayItems(numbers,currentArrayLength,"numbers");// numbers: 10, 11, 12, 13, 14.}voidpushToDynamicArray(int**array,intvalue,int*currentLength,int*currentCapacity){printf("BEFORE currentLength: %d \n",*currentLength);printf("BEFORE currentCapacity: %d \n",*currentCapacity);intnewCapacity=*currentCapacity;*currentLength=*currentLength+1;// adding 1 item to the lengthif(*currentLength>*currentCapacity){*currentCapacity*=2;// doubling capacity strategyint*tempArray=(int*)realloc(*array,sizeof(int)*(*currentCapacity));// resizing with reallocif(tempArray==NULL){printf("realloc failed.\n");exit(1);}*array=tempArray;}(*array)[(*currentLength)-1]=value;printf("AFTER currentLength: %d \n",*currentLength);printf("AFTER currentCapacity: %d \n",*currentCapacity);}voidprintArrayItems(int*array,intarrayLength,chararrayName[]){printf("----\n");printf("%s: ",arrayName);for(inti=0;i<arrayLength;i++){printf("%d",array[i]);if(i+1==arrayLength){printf(".\n");}else{printf(", ");}}printf("----\n");}
Improvements - "Struct Implementation":
After understanding the concept, let's implement a struct called DynamicArray.
We could create a struct to encapsulate both arrayCapacity and arrayLength for better organization and to simplify the management of the dynamic array.
Not bad, but personally I do it differently: when I write a function, I first calculate the size of the allocated memory, and then fill this memory. Usually this can always be done, if we know the algorithm for filling, we can make the same algorithm for calculating the size
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Top comments (1)
Not bad, but personally I do it differently: when I write a function, I first calculate the size of the allocated memory, and then fill this memory. Usually this can always be done, if we know the algorithm for filling, we can make the same algorithm for calculating the size