Dynamic memory allocation
As a compiled language, in normal situations your datatypes will be evaluated when your program is being compiled. However, we can also assign values to our datatypes while the program is running. Imagine a situation where only the user and not the developer knows how big the array needs to be. In such situations, the programmer cannot declare the array while he writes his code. He must ask the user for the array size while his program is running. In interpreted languages, this would be no problem at all, since you can allocate variables while the program is running, but in C, a compiled language, we must go through extra steps to create dynamic variables.
Malloc and free
When you declare a variable normally, it will be stored in a memory area of your program called the stack. A dynamic variable however will be stored in a memory area called heap. The more you allocate in heap memory, the less you can allocate in the stack without raising issues and warnings, and vice versa. Thats because the heap and the stack grow in opposite directions, and if they get close to each other, thats when problems start.
The basic functions C provides for us to allocate dynamic memory are malloc and free. Malloc(from memory allocation) will allocate a pointer with the size in bytes you specify in the as argument to the function:
void *p = malloc(25);
Malloc now has allocated a n bytes (25 in the example above) to be stored in the heap of your program and returns the adress where you allocated it in the pointer. The problem here is, those bytes will remain there as long as your program is running, and it doesnt matter if you are outside of the scope you declared your pointer or even if your function has returned. Unlike the stack, these bytes will remain in the heap, consuming resources of your program. For instance, in a situation like below:
int main()
{
f();
return 0;
}
f() {
void *p = malloc(4);
}
Altough our function f() where we allocated 4 bytes to the pointer p has returned, those bytes will still be allocated in the heap, unlike when we do it in the stack.
In this context, we must use the function free, passing the pointer which was initialized with the malloc function to release these bytes from the heap.
f() {
void *p = malloc(4);
free(p);
}
Thats one aspect of C which is different from higher level programming languages. You must remember to use free to release memory allocated on the heap, unlike programming languages like java or c# which take care of everything for you. This could be a good thing or bad sometimes.
Altough malloc can only understand the size in bytes you need to allocate as an argument, and doesnt know about different datatypes, it is possible to format the arguments you pass as a datatype value instead of a raw number, so we dont have to calculate the exact amount of bytes everytime:
void *p = malloc(sizeof(int));
Malloc allocates memory in contiguous memory adresses. So lets say you you've allocated 10 bytes and 20 bytes right after. If you free the first pointer, and allocate a new pointer with 15 bytes, these bytes will be allocated in after the second pointer(with 20 bytes in size). This can leave gaps in your heap, but like we have seen in structs, it is a faster way to acess memory adresses with one acess, instead of spreading the allocated bytes and performing more than acess through different adresses.
But what about dynamic allocation and user interaction we menationed previously? Yeah, lets ask our user whats the size of the array while the program is running(at runtime):
int *array;
int p;
printf("Declare the array size\n");
scanf("%d", &p);
array = malloc(p * sizeof(int));
for (int i = 0; i < p; i++) {
int val;
printf("declare the value of position %d\n", i);
scanf("%d", &val);
array[i] = val;
}
for (int i = 0; i < p; i++) {
printf("array[%d] = %d\n", i, array[i]);
}
return 0;
Nice isnt. Scanf will get the input value adress and return the value at runtime and malloc will allocate the array size at runtime. I believe you, like me, have imagined why is this necessary, for example, if we want to declare a name but we dont know beforehand how big it is, if we declare the array with 100 elements, its possible some space is just being wasted, at least i myself know a short list of names with more than 100 characters lol. On the other hand, if we declare an array with only 3 elements, well, i believe your name might not be so short. Why not let the user declare the array size then? In the previous example, our user not only can specify the array size with malloc, but also the elements values at each position with the scanf in the for loop.
In higher level programming languages we would have so much less work wouldnt we? Lets take a look how we would do it in perl since i have my eclipse opened:
print("Input array size\n");
$size = <>;
@
array = ();
for($i = 0; $i < $size; $i++) {
$element = <>;
push@
array, $element;
}
print (@array);
Sweet isnt? to be honest we could have done this perl code even more compact than it is, but i wanned you to see and trace parallels between this code and the C code you have saw. In perl, not only the aray and variables are already dynamic declared, but they also can contain any type of elements, not only int or float and such, so the same aray could contain any type of data, form numbers to strings or even arrays and hashs. Why am i quoting this? Well, just another tought which came to my mind, when you learn a few languages you will also compare them, if you already dont, but i think its still usefull, afterall pick the right tool for the right job, and anyways, perl also has a good amount of C in its core, and so do many languages. C is in the core of many technologies, so it will be very usefull to learn and analyse it as a debugger.
Now, lets just say we ned more heap memory later in the code. We can resize the memory allocated with command realloc if we must modify it later:
void *p = NULL;
p = realloc(p, 16);
Common Errors with dynamic memory allocation
When you allocate dynamic memory, its not possible to free only a portion of the memory which was allocated. We could free using pointer arithmetic, but the thing is, we can only point to the adress of the memory allocation adress. So it would be valid to use this arithmetic on free:
int *array;
array = malloc(25);
free(array + 2 - 3 + 1);
But if we try to release only part of this memory we would get nasty errors:
int *array;
array = malloc(25);
free(array + 2);
Acessing values which had been freed may also generate problems to your code. Some of them are silent. For example, lets initialize a value and free it right after, then try to acess it:
int *array;
array = malloc(25);
array[1] = 25;
free(array);
printf("%d", array[1]);
return 0;
The chances are high your compiler will not raise any warnings, however you program might not be happy to acess dirty values values at some point.
Another bug harder to spot than the previous is to try acess values beyond the memory allocated. Lets take the sample code from before:
int *array;
array = malloc(25);
array[1] = 25;
free(array);
Now lets say we try to acess element 30 of the array, or element -1. If your program is holding no values at that space, the bug may run quietly during your code lifecicle, but imagine if you assign a value there and theres already an important value at that space. An important value could erase or even your program could terminate witx exceptions!
When you are using free, you must use a pointer previously allocated with malloc. Passing a pointer not dinamically allocated may cause the program to terminate before it starts or later(which is worst).