How To Get The Size Of An Array
Updated:
Sometimes we want to know the size or the number of elements of an array, perhaps because we want to create a for loop that processes each item of this array. As we will see, we cannot always implement this calculation. We can find different scenarios, and sometimes we can only fall back on tricks.
The array is statically allocated
int arr_sta[100];
int arr_sta_init[] = {2, 3, 5, 7, 11}
In this case, the array resides on the stack, and we can exactly know the number of elements as long as we are in the scope in which we define it, as we will see later.
Whether we define an array by indicating the number of elements or using a brace-enclosed initializer, we can know its number of elements thanks to the sizeof operator. We simply divide the total memory size of this array by the size of its first element, and we get the number of elements.
size_t arr_len = sizeof(arr_sta) / sizeof(arr_sta[0]);
By implementing this calculation in a macro, we can make our life even easier. So, we can easily create a for loop like this:
#define ARR_LEN(x) (sizeof(x) / sizeof(x[0]))
int main() {
int arr_sta[120];
for (size_t i = 0; i < ARR_LEN(arr_sta); i++) {
/* process arr_sta[i] */
}
}
The array is a pointer to a stack-allocated array
#define ARR_LEN(x) (sizeof(x) / sizeof(x[0]))
void process_buf(char *buf, int len) {
for (int i = 0; i < len; i++) {
/* process buf[i] */
}
}
int main() {
int buf[100];
process_buf(buf, ARR_LEN(buf));
}
In this example, we define the array buf and pass its base address to the function foo. Now, the only way foo can process buf element by element is to give the number of elements of buf to the function. As long as the number of elements of buf is not a globally available variable or constant, of course. That’s the only way we have to implement the foo function properly if we want to keep things simple and avoid building structures around buf.
The array is a pointer to a dynamically allocated memory block
int *arr_dyn = malloc(sizeof(*arr_dyn) * LEN);
In this case, unless we have, of course, direct access to LEN, the only way we can find the length of arr_dyn is through a gimmick. The only thing we can do is to cheat a little bit. We can store the value of LEN directly in the newly allocated block of memory.
Now in the arr_dyn pointer, we do not directly store the address returned by malloc, but the address incremented by one. This way, we know that just an integer before the first element of arr_dyn, we can find its length, and at the same time, arr_dyn behaves like a standard pointer to an array.
Let’s see an example of this implementation and let’s discuss it.
#include <stdio.h>
#include <stdlib.h>
void foo(int *arr) {
/* let's scan the array: we need to retrieve its length */
for (int i = 0; i < arr[-1]; i++) {
printf("arr[%d]: %2d\n", i, arr[i]);
}
}
int main() {
const int LEN = 10;
int *arr_dyn = ((int *)malloc(sizeof(*arr_dyn) * (LEN + 1))) + 1;
arr_dyn[-1] = LEN;
/* let's set some values (now we use the LEN constant) */
for (int i = 0; i < LEN; i++) {
arr_dyn[i] = i * i;
}
/* now let's see if the trick works */
foo(arr_dyn);
}
With this little example program we get this output:
arr[0]: 0
arr[1]: 1
arr[2]: 4
arr[3]: 9
arr[4]: 16
arr[5]: 25
arr[6]: 36
arr[7]: 49
arr[8]: 64
arr[9]: 81
So we see that the trick of storing the length of the array in the newly allocated block of memory worked. Let’s break down the code a bit.
As argument to malloc, we pass sizeof(*arr_dyn) * (LEN + 1). It’s a very versatile expression. We calculate at runtime the size of an element of arr_dyn by dereferencing the pointer. Then, we multiply that value by LEN + 1. This way, we have the space we need. But that’s not all. In arr_dyn, we need to store the base address of the array, so we need to increment the value returned by malloc by one since we want to dedicate the first element to stash its length.
Consequently, we have to cast the return value of malloc before we can make the increment. This is how pointer arithmetic works. In the end, to access the memory location in which to store (and subsequently read) the value LEN, it’s sufficient to use the notation in square brackets and access the index -1. It’s worth noting that this notation is perfectly legal in C. Again, it’s simply pointer arithmetic.
Just a warning: if you choose to take this way, when you deallocate this memory, you will have to remember that it starts one before the arr_dyn address!
free(arr_dyn - 1);