A linked-list based, easy to use and well documented, C library for managing memory in your programs.
Disclaimer: The aim of this project, and it's name, already exist as other repositories, but the contents of this repository are original and the work of several contributors.
To install with Makefile, first ensure you have make
installed, clone the git repository, change into the projects directory and run the command sudo make install
.
$ git clone https://github.com/Khhs167/memstack.git
$ cd memstack
$ sudo make install
To remove the library, ensure you are inside the repository's folder and uninstall with the following make command.
$ sudo make uninstall
The AUR (Arch User Repository) is a community-driven repository for Arch Linux. The installation instructions below are only available to those to use the Arch Linux operating system.
To install memstack from the AUR you need the AUR link (https://aur.archlinux.org/libmemstack-git.git) from the memstack AUR page.
After copying, you'll need to clone, and build the library with makepkg -si
$ git clone https://aur.archlinux.org/libmemstack-git.git
$ cd libmemstack-git
$ makepkg -si
However, most people will use a AUR client which handles everything for us:
$ yay -S libmemstack-git
NOTE: this example uses yay
To compile a program with the memstack library use the -lmemstack
compiler flag like shown below.
$ gcc program.c -lmemstack
(or with clang)
$ clang program.c -lmemstack
The memstack library has a type called memstack*
used to keep track of allocations.
To create a memstack, you can use the function msnew()
as shown below.
#include <memstack.h>
// Example C program using memstack
int main() {
// We crate a new memstack using the function msnew()
memstack* ms = msnew();
// ...
msfree(ms); // Free the memstack
return 0;
}
It's important to note that msfree(memstack*);
will free the entire memstack (including the memstack itself).
This means the memstack we define ms
will no longer be usable after msfree(ms)
is called.
msnew
(usage: msnew()
) will return a memstack pointer that can be used in conjunction with other memstack functions to
add allocations, deallocate, clear, and free more easily.
// Example of creating a new memstacking using msnew().
memstack* example_memstack = msnew();
msfree
(usage: msfree(memstack*)
) will free all the allocations logged on a memstack and free the memstack passed into it.
It's recommended this function be called to free any initialised memstack at the end of the program.
// Example of creating (using msnew(), and freeing (using msfree()) a memstack.
memstack* example_memstack = msnew(); // Create memstack.
msfree(example_memstack); // Destroy memstack.
Using msfree
on a memstack*
will deallocate the memstack*
itself.
This means trying to use a memstack*
after it has been freed via msfree
will cause undefined behaviour.
msalloc
(usage: msalloc(memstack*, int)
) will allocate a given size and add it onto the given memstack.
msalloc
will return a void*
which can be used, manipulated, and eventually freed using msfree
.
memstack* example_memstack = msnew(); // Create a new memstack for allocations.
// Example of allocating onto a memstack.
int* a = msalloc(example_memstack*, sizeof(int));
int* b = msalloc(example_memstack*, sizeof(int));
int* c = msalloc(example_memstack*, sizeof(int));
// Freeing the memstack.
msfree(example_memstack);
msclear
(usage: msclear(memstack*)
) will free all the allocation on a memstack but will not destroy the memstack itself.
This function does a similar job to msfree
but does not destroy the memstack*
after.
This allows the memstack*
to be used for further allocations even after freeing everything.
memstack* example_memstack = msnew(); // Create a new memstack for allocations.
// Example of allocating onto a memstack.
int* a = msalloc(example_memstack*, sizeof(int));
// Clearing the memstack.
msclear(example_memstack); // a has been freed. Memstack is empty.
// Freeing the memstack.
// You must still clear the memstack itself so msfree still has to be called after msclear.
msfree(example_memstack);
mspush
(usage: mspush(memstack*, void*)
) allows you to add allocations to a memstack*
without having to allocate using msalloc
.
mspush
will not allocate space, but instead can be used to add already allocated space to a memstack*
.
This is useful for adding allocations which you may not have easy control over, such as when using a library.
memstack* example_memstack = msnew(); // Create a new memstack for allocations.
// Create an allocation (without msalloc)
int* a = malloc(sizeof(int));
// Add the allocation to the memstack
mspush(example_memstack, a);
// Freeing the memstack.
msfree(example_memstack);
mspop
(usage: mspop(memstack*)
) will remove the latest element of a give memstack*
and return the element's allocated space.
This allows you to remove allocations from the memstack*
and handle it yourself.
It's important to note that calling mspop
will remove the allocation from the memstack*
, and therefore the allocation will not be freed when msfree
is called.
memstack* example_memstack = msnew(); // Create a new memstack for allocations.
// Example of allocating onto a memstack.
int* a = msalloc(example_memstack*, sizeof(int));
// Remove "a" from the memstack.
int* a = mspop(example_memstack);
// Freeing the memstack.
msfree(example_memstack);
// Since "a" is no longer being handled by the memstack, you must free it yourself.
free(a);
msrollback
(usage: msrollback(memstack*, int, int)
) can be used to remove multiple elements from a memstack*
.
msrollback
will begin removing elements starting from the latest element, it will remove only the given number of elements, from the given memstack*
.
Elements can be removed both "destructively" (i.e. the memory is cleared), or "non-destructively" (i.e. the memory is not cleared).
If the memory is not cleared, you, the programmer, must handle freeing the memory yourself.
An example of msrollback
is shown below.
For this example TRUE
is defined as 1
and FALSE
as 0.
memstack* example_memstack = msnew(); // Create a new memstack for allocations.
// Example of allocating onto a memstack.
int* a = msalloc(example_memstack*, sizeof(int));
int* b = msalloc(example_memstack*, sizeof(int));
int* c = msalloc(example_memstack*, sizeof(int));
// Remove two elements destructively.
msrollback(example_memstack, 2, TRUE); // "b" and "c" are removed and freed!
// Remove one element non-destructively.
msrollback(example_memstack, 1, FALSE); // "a" is removed (but not freed)!
// Freeing the memstack.
msfree(example_memstack);
// We must handle freeing "a" ourself because it doesn't belong to the memstack
free(a);
The functions covered above are explained using a user created memstack (created through msnew()
).
However, the library also has a global memstack internal to the library.
This allows for allocations to be made from anywhere in a codebase.
In order to start using the global memstack, the user must call the msinit()
function.
This function does not return anything, and initialises the global memstack.
After initialising the global memstack, you can manipulate the memstack using the same functions explained above.
However, instead of using our user created memstack, you can pass in the macro GLOBAL_MEMSTACK
.
The function called will know you're referring to the global memstack and manipulate this instead.
An example of using the global memstack is shown below.
#include <memstack.h>
#include <stdlib.h>
int main() {
msinit(); // Initialise the global memstack.
// Allocating onto global
int* example1 = msalloc(GLOBAL_MEMSTACK, sizeof(int));
// Clearing global
msclear(GLOBAL_MEMSTACK);
msfree(GLOBAL_MEMSTACK); // Free global memstack.
}
NOTE: GLOBAL_MEMSTACK
is a macro for NULL
. Passing NULL
into memstack functions will also manipulate the global memstack.
Contributing helps keep this library safe and up to date. If you want to help, why not create an issue?
If you find the same library elsewhere(maybe even with the same name), that's just because it's not an original idea.