When we use memory in our computers, memory is stored in a few different places depending on the situation. Generally, this involves a stack and a heap. This is a representation of how a program stores data in memory:
+--------------------------+
| machine code |
+--------------------------+
| globals |
+--------------------------+
| heap |
| ↓ |
| |
| |
| |
| |
| |
| |
| ↑ |
| stack |
+--------------------------+
These two locations in memory operate based on two types of memory allocation: static allocation and dynamic allocation.
+--------------------------+
| machine code |
+--------------------------+
| globals |
+--------------------------+
| heap | <------- Dynamically allocated
| ↓ |
| |
| |
| |
| |
| |
| |
| ↑ |
| stack | <------- Statically allocated
+--------------------------+
Fun fact: If too much memory is allocated on the stack, it will “overflow” into the heap. This causes what is known as a “stack overflow.” (Not to be confused with the website.)
In the end, the biggest difference between static and dynamic memory is scope.
In static memory, data sticks around within the “curly braces” it was created in. Once either a function ends, a loop completes, or the program terminates, the static memory associated with that scope is returned to be used for other programs.
For example, the integer n
in this small program will
only exist in funny_function()
, and that information is
lost when the function ends:
#include <stdio.h>
void funny_function(void)
{
int n = 6;
("The value of n is %d!\n", n);
printf}
int main(void)
{
();
funny_function("I can't see the value of n in main()...\n");
printf
return 0;
}
In dynamic memory, data sticks around as long as you want. You can allocate memory and use it at any time, in any function, free-of-charge. You now have full control of where in your program a piece of memory can be used.
In C, we have a few functions available in the stdlib.h
library, one of the most important being malloc()
(short
for memory allocation).
malloc()
thing?malloc()
takes one parameter: The amount of memory (in
bytes) you would like to allocate.
What if I don’t know how many bytes I need? Can I put in a random number? No. You must always decide how much memory you want to allocate.
Example:
// Allocate the memory needed to store 100 integers.
(sizeof(int) * 100); malloc
If successful, malloc()
returns an address to
the memory it allocated. We use a pointer to store this address.
// Now "numbers" is a pointer to the base address of 100 integers.
// In other words, "numbers" is an array of 100 integers.
int *numbers = malloc(sizeof(int) * 100);
If malloc()
fails, it will return NULL
instead.
// Attempting to allocate memory for 10,000,000,000 integers.
// There is very likely not enough memory, so malloc() returns NULL.
int *numbers = malloc(sizeof(int) * 10E10);
malloc()
ing!#include <stdlib.h>
int main(void)
{
while (1)
{
(sizeof(int));
malloc}
return 0;
}
Nooooo! This is all wrong.
This program is continuing to allocate memory without returning it to other programs. It is such a hog, leaking memory all over the floor. Don’t be a menace to your computer.
We need some way to return this memory when we’re done with it. Since it isn’t statically allocated, this is not an automatic process.
free()
free()
can be used to deallocate memory. It takes one
parameter: a pointer to dynamically allocated memory.
For example:
int *pointer = malloc(sizeof(int));
(pointer); free
This is all it takes to dynamically allocate memory to an integer, then deallocate it once finished using it.
Note: Only use
free()
with dynamically allocated memory. Doing something like:int x; (&x); free
will result in disastrous consequences. You have been warned.
Going to the previous example, this would be better:
#include <stdlib.h>
int main(void)
{
int *pointer;
while (1)
{
= malloc(sizeof(int));
pointer (pointer);
free}
return 0;
}
This still causes an infinite loop, but at least you won’t be hogging all the memory anymore.
calloc()
:
The Over-Accomplishing Siblingcalloc()
is like the sibling of malloc()
that always tries to do more. They are both related, but have
differences on what they do. calloc()
is short for
“contiguous allocation,” and it will
allocate memory just like malloc()
, but also
initialize the memory to 0, so you have no garbage
values right off the bat. In this way, calloc()
can be
useful for making, for example, arrays that would have been initialized
anyway.
calloc()
takes a quantity and a size. For example:
// Allocate space for 10 integers, and initialize it to 0.
int *numbers = calloc(10, sizeof(int));
realloc()
: I Changed My
MindThere is also a cousin to malloc()
, called
realloc()
, which allows you to re-allocate
memory. This can be extremely useful if you, for example, want to take
an array with an initial size, but resize it later.
The basic syntax of realloc()
is to provide a pointer to
dynamically allocated memory and the new amount of memory that should be
allocated. As a result, realloc()
returns a new pointer to
dynamically allocated memory. For example:
// Assume "array" is already a pointer to dynamically allocated memory.
int *resized_array = realloc(array, sizeof(int) * 100);
You can consider the following two pieces of code to be equivalent,
but notice how much shorter the version with realloc()
is!
Version using malloc()
and
free()
:
// Allocate space for 10 integers
int *array = malloc(sizeof(int) * 10);
// Assume you put values in this array, and now you decided you wanted space
// for 20 integers in the array. You will need to allocate a new array, copy
// the integers from the old array to the new array, then free the old array.
int *new_array = malloc(sizeof(int) * 20);
for (int i = 0; i < 10; i++)
{
[i] = array[i];
new_array}
(array);
free
// Now we can say that "new_array" contains the same data as before, but with
// space for 20 integers instead of 10!
Version using realloc()
:
// Allocate space for 10 integers
int *array = malloc(sizeof(int) * 10);
// Assume you put values in this array, and now you decided you wanted space
// for 20 integers in the array. Let's use realloc() to combine many steps
// into a single function!
int *new_array = realloc(array, sizeof(int) * 20);