Malloc and free both interact with the heap. The heap can grow if needed. Malloc and free are both implemented in library. This means that in C and in C++, you need to import stdlib.h or cstdlib respectively.

There are three types of memory allocation, that are made explicit for so called “low-level” programming languages like C, C++, Rust or zig.

Static Memory Allocation

Static memory allocation is allocation at compile time. When the size of a variable is known at compile time, for example a global integer declared at the top of a file like so:

int x = 0;
 
int main(){
	printf("Hello world!");
}

X will have a memory location assigned to it at compile time. It will have a lifetime for the duration of the program, and global scope. This is generally best for constants that do not need to be changed and are needed at all times of the program. Examples could include a gravitational force constant for a physics simulation for example.

Automatic Memory Allocation

Automatic memory allocation occurs when a local variable needs to be allocated or when a function is called onto the stack. The memory needed for a function call is automatically allocated to the stack, alongside with the local variables needed.

This means that the lifetime of the variable is only as long as the function call though. Once the function returns, any automatic variables are destroyed. Using their addresses or pointers after the function returned leads to a dangling pointer and undefined behavior.

This for example, is an example of such a function that would most likely lead to a runtime error:

int* badFunction() {
    int local = 10;
    return &local;
}

Dynamic Memory Allocation

Dynamic memory allocation is used when more flexibility is needed and the two previous methods don’t cut it. In C and C++, this is generally through the use of malloc, or C++‘s object oriented new(), which calls constructors and may rely on [malloc], though its use is not guaranteed.

Dynamic memory allocation is significantly more involved, since it is programmer-managed, but is incredibly flexible. It comes with its own significant amount of pitfalls, including dereferencing null pointers, segfaults related to that, memory leaks, dangling pointers and so on.