Skip to content

Memory manager

Ilya Kashnitskiy edited this page Jan 21, 2020 · 5 revisions

One of the most important part of my library. Designed to solve the following problems:

  • Avoid the checking result after every malloc() and for every function which called malloc() inside!
  • Get easy way to free() all allocated memory before exit() (especially in case of critical errors and abort program);

This is the header with API: ft_memory_manager.h and this is the implementation folder: sources/ft_memman/

  • Error management

Special for memory manager I wrote a simple error handler. But now, it's independent part of library, so you can use it, when it convenient.

Check ft_error.h and ft_error.c

  • void ft_error_print(const char *msg)

Just write msg to FD_STDERR (from libft.h) previously pushing a output buffer.

You may not immediately realize, but it's very useful to not #include <unistd.h>, write ft_strlen(), push buffer by hands and etc. when you wona just print error, but thats true!

  • void ft_error_exit(const char *msg, int exit_code)

This func do the same but also it call exit(exit_code) (and abort you app, obviously!).

  • void ft_error_free_exit(const char *msg, int exit_code)

This func do the same but also it call ft_memman_clean() (free() all memmory allocated by malloc()) before exit(exit_code).

Memory manager API

  • void ft_memman_init(void)

This function must be called at the beginning of program. Inside the memory manager uses a static dynamic array. It's just a simple repository for storing every pointer to the memory that has been allocated. But, since this is a dynamic array (allocate memory until RAM runs out), it also requires initialization, allocating memory for itself. In case malloc() inside this func return NULL, ft_error_exit(ERR_MEMALLOC_MSG, MEMERR_CODE) will be called!

MEMERR_CODE defined in ft_memory_manager.h (0 by default). If you want your application to return NOT 0 in case of a memory allocation failure, change it!

  • void ft_memman_clean(void)

This function must be called at the end of program (or before exit()). It iterates over all stored pointers and free() them. After it free memory under himself. So, if you are not a fool, all allocated memory will be guaranteed to be freed!

WATCH OUT!!! This mechanic is designed to conveniently handle errors and create easy, beautiful and readable code! If you use it to remove memory leaks (cause valgrind will not show you anything) then your program still has leaks! DO NOT CHEAT!!!

To check on leaks put breakpoint in debuger at ft_memman_clean() (or comment out cycle inside)!

  • ft_malloc(); ft_remalloc(); ft_free()

You should use these functions instead standard! Really, with my library there is no reason to use malloc() realloc() and free()

Even if you turn off memory manager, you have no need to change ft_malloc() to malloc()! Check malloc.c!

All you need to know is that either ft_malloc() and ft_remalloc() will allocate memory for you, or they will call ft_error_free_exit(ERR_MEMALLOC_MSG, MEMERR_CODE)! And ft_free() either free() the requested memory, or prints an error message (if this memory was not allocated) and returns control to the program (not fault like standard).

  • Should I do something if I disable memory manager?

No, all functions will be work correct. Check memory manager sources to find answer.

  • #define values

#define Description
# define INIT_MM_SIZE 128 Under how many pointers the memory will be allocated during initialization.
# define MM_MAX_INCREMENT 1024 Maximum step to increase memory manager size
# define MM_DIFF_FOR_TRIM 64 If after ft_free() curlen of mem_manager < max_size / 2 - MM_DIFF_FOR_TRIM mem_manager size will be halved
  • Example

Let me show you how convenient it is!

Let's write a function that creates a two-dimensional array (simple matrix).

  • Without my library
void	delete_matrix(int **matrix, size_t rows)
{
	for (size_t i = 0; i < rows; i++)
		free(matrix[i]);
	free(matrix);
}

int	**init_matrix(size_t rows, size_t cols)
{
	int **result;

	result = malloc(sizeof(int *) * rows);
	if (result == NULL)
		return (NULL);
	for (size_t i = 0; i < rows; i++)
	{
		result[i] = malloc(sizeof(int) * cols);
		if (result[i] == NULL)
		{
			delete_matrix(result, i);
			return (NULL);
		}
	}
	return (result);
}

int	main(void)
{
	int **matrix;

	matrix = init_matrix(10, 10);
	if (matrix == NULL)
	{
		ft_printf("Error!");
		return (0);
	}
	ft_printf("OK!");
	delete_matrix(matrix, 10);
	return (0);
}
  • With using my library
void	delete_matrix(int **matrix, size_t rows)
{
	for (size_t i = 0; i < rows; i++)
		ft_free(matrix[i]);
	ft_free(matrix);
}

int	**init_matrix(size_t rows, size_t cols)
{
	int **result;

	result = ft_malloc(sizeof(int *) * rows);
	for (size_t i = 0; i < rows; i++)
		result[i] = ft_malloc(sizeof(int) * cols);
	return (result);
}

int	main(void)
{
	int **matrix;

	ft_memman_init();
	matrix = init_matrix(10, 10);
	ft_printf("OK!");
	delete_matrix(matrix, 10);
	ft_memman_clean();
	return (0);
}

In my version, main() is 5 lines shorter, and init_matrix() is 9 lines shorter! And my code is MUCH more readable and more beautiful!

Now imagine that you are develop a large project with a large level of nesting functions! If you still think that there is no need for my memory manager, I have no reason to talk to you!

GO TO NEXT PAGE --->