8

I am developing a project in C, and I need to free the allocated memory and also close all the open files before it exits.

I decided to implement a clean function that will do all this stuff and call it with atexit because there are a lot of possible exit scenarios.

The problem is that atexit doesn't allow me to set functions with parameters, so I can't send to clean the pointers that need to be freed in the end of the process.

So I need to declare as global variables every pointer that may need to be freed, and every file that may remaining open in the program? (I already did that but doesn't looks good) or does exist a similar function to atexit that allows to send parameters? or more probably there is another way that I am missing?

BenMorel
  • 34,448
  • 50
  • 182
  • 322
sir psycho sexy
  • 780
  • 7
  • 19
  • 1
    You only need one variable that points to you memory pool. That seems acceptable. – this Jul 24 '14 at 09:40
  • 3
    If you're on a modern consumer operating system then all resources will be released when you exit. That means all allocated memory will be freed, all open files will be closed, etc. – Some programmer dude Jul 24 '14 at 09:41
  • 1
    Also, if you have e.g. files that need to be open during the whole process runtime, then you probably also access it in multiple places, and that means it's already a global variable, or at least global in one translation unit, and then you can have a function to close just that file that are called from a global `clean_all` function. The same with memory you allocate. – Some programmer dude Jul 24 '14 at 09:44
  • On the other hand, if you just open files and then don't close them or allocate memory but never free it, even when you're done with the files or memory, you have resource leaks, and that will come back and bite you in the rear parts sooner or later. – Some programmer dude Jul 24 '14 at 09:45
  • @this Maybe it's ok in that way, but I am trying to do in the best way possible because it is a project for the university. – sir psycho sexy Jul 24 '14 at 09:51
  • @JoachimPileborg Yes, I know, but I am asked to do it anyway. – sir psycho sexy Jul 24 '14 at 09:52
  • Do what @JoachimPileborg recommended. – this Jul 24 '14 at 09:53

2 Answers2

6

Using a static pointer inside a function:

#include <stdio.h>
#include <stdlib.h>

void atexit_clean(void *data);

static void clean(void)
{
    atexit_clean(NULL);
}

void atexit_clean(void *data)
{
    static void *x;

    if (data) {
        x = data;
        atexit(clean);
    } else {
        free(x);
    }
}

int main(void)
{
    int *a = malloc(sizeof(int));

    atexit_clean(a);
    return 0;
}

Another method using a single global variable: you can store all objects to be freed in an array of pointers or a linked list, this example uses realloc (doesn't check (m/re)alloc for brevity):

#include <stdio.h>
#include <stdlib.h>

static void **vclean;
static size_t nclean;

void atexit_add(void *data)
{
    vclean = realloc(vclean, sizeof(void *) * (nclean + 1));
    vclean[nclean++] = data;
}

void clean(void)
{
    size_t i;

    for (i = 0; i < nclean; i++) {
        free(vclean[i]);
    }
    free(vclean);
}

int main(void)
{
    int *a, *b, *c;
    double *d;
    int e = 1;

    atexit(clean);
    a = &e;
    b = malloc(sizeof(int));
    atexit_add(b);
    c = malloc(sizeof(int));
    atexit_add(c);
    d = malloc(sizeof(double));
    atexit_add(d);
    return 0;
}
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • You should move `atexit(clean_all);` to the start of the `main()` function. For a realistic example there could be operations that terminate the program between the calls to `atexit_add()`. – Kijewski Jul 24 '14 at 10:23
  • 1
    @Kay, that depends on whether you want to leave in a clean way or aborting, but in general you're right, edited. – David Ranieri Jul 24 '14 at 10:36
  • `atexit_add()` should check for `realloc()` failure and return an error indication to the caller. – Nisse Engström May 22 '16 at 14:56
4

There is no way to pass any parameters to atexit(), so you're stuck using global variables.

When your program terminates normally, through exit() or by returning from main(), it will automatically flush and close any open streams and (under most operating systems) free allocated memory. However, it is good practice to explicitly clean up your resources before the program terminates, because it typically leads to a more structured program. Sometimes the cleanest way to write your program is to just exit and leave the cleanup to the implementation.

But be warned that you should always check the return value of fclose(). See "What are the reasons to check for error on close()?" for an anecdote about what could happen when you don't.

Community
  • 1
  • 1
Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
  • 1
    Just note that the C standard does not *require* implementations free allocated memory. Indeed, a common problem for the TI-89 series is C programs not cleaning up before exit, and the OS did *not* clean up, thus repeated executions of a leaky program would exhaust RAM on the calculator. – Drew McGowen Jul 24 '14 at 16:16