-1

Consider this program:

int main(void)
{
    int* i = malloc(sizeof(int));
    int* j = malloc(sizeof(int));
}

However this is a naive approach, because malloc may fail and the pointers are not free'd.

So you can do this:

int main(void)
{
    int* i; 
    int* j;

    if ((i = malloc(sizeof(int)) < 0)
    {
        return -1;
    }
    if ((j = malloc(sizeof(int)) < 0)
    {
        free(i);
        return -1;
    }

    free(i);
    free(j);
}

However I consider this very error-prone. Consider having to assign 20 pointers, in the last malloc error case, you have to free 19 variables and then return -1.

I also know atexit, which can help me to write it like this:

int* i; 
int* j;

void del_i(void)
{
    free(i);
}

void del_j(void)
{
    free(j);
}

int main(void)
{
    if ((i = malloc(sizeof(int)) < 0)
    {
        return -1;
    }
    else
    {
        atexit(del_i);
    }

    if ((j = malloc(sizeof(int)) < 0)
    {
        return -1;
    }
    else
    {
        atexit(del_j);
    }
}

Which is better, but I dislike having to declare all pointers as global. Is there some way to combine these two approaches, basically:

  1. Having destructors for pointers, which can be either executed directly or be used with atexit.
  2. Having pointers local to functions.
hgiesel
  • 5,430
  • 2
  • 29
  • 56

6 Answers6

4

free on NULL is defined to be a safe no-op. So a non-jumping variation could be:

int *i = malloc(sizeof(int)); 
int *j = malloc(sizeof(int));

if(i && j)
{
    // do some work
}

free(i);
free(j);
Tommy
  • 99,986
  • 12
  • 185
  • 204
3

First, this will not detect malloc failure:

if ((i = malloc(sizeof(int)) < 0)
{
    return -1;
}

malloc returns NULL on failure, not a negative number.

Second, atexit is good for cleaning up static and global objects. It is not a good idea to make local objects global only to use them inside atexit.

A better approach is to make a struct for all related pointers that you need to allocate in an all-or-nothing unit, define a function for freeing them all at once, and write a function that allocates them one by one with memory checking on each allocation:

typedef struct AllOrNothing {
    double *dPtr;
    int *iPtr;
    float *fPtr;
    size_t n;
} AllOrNothing;

void freeAllOrNothing(AllOrNothing *ptr) {
    free(ptr->dPtr);
    free(ptr->iPtr);
    free(ptr->fPtr);
    free(ptr);
}

int allocateAllOrNothing(size_t n, AllOrNothing **res) {
    *res = malloc(sizeof(AllOrNothing));
    if (*res == NULL) {
        return -1;
    }
    // Freeing NULL is allowed by the standard.
    // Set all pointers to NULL upfront, so we can free them
    // regardless of the stage at which the allocation fails
    (*res)->dPtr = NULL;
    (*res)->iPtr = NULL;
    (*res)->fPtr = NULL;
    (*res)->n = n;
    (*res)->dPtr = malloc(n*sizeof(double));
    if ((*res)->dPtr == NULL) {
        free(*res);
        *res = NULL;
        return -1;
    }
    (*res)->fPtr = malloc(n*sizeof(float));
    if ((*res)->fPtr == NULL) {
        free(*res);
        *res = NULL;
        return -1;
    }
    (*res)->iPtr = malloc(n*sizeof(int));
    if ((*res)->iPtr == NULL) {
        free(*res);
        *res = NULL;
        return -1;
    }
    return 0;
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
0
int main(void)
{
int* i = NULL; // Init with NULL otherwise free on none NULL possible
int* j = NULLL;

if (!(i = malloc(sizeof(int)))
{
    goto exit;
}
if (!(j = malloc(sizeof(int)))
{
    goto exit;
}
...
exit:
    free(i);
    free(j);
    ...
    return err;
}

This is something you can solve with goto statements.

Schafwolle
  • 501
  • 3
  • 15
0
int main(void)
{
    int* i = NULL; 
    int* j = NULL;
    bool success = false;

    do {
        i = malloc(sizeof(int));
        if (NULL == i) break;

        j = malloc(sizeof(int));
        if (NULL == j) break;

        success = true;
    } while (0);

    if (!success)
    {
        printf("Something failed!");
    }
    else
    {
        printf("All succeeded!");
        // Do more work
    }

    free(i);
    free(j);
    return (success? 0 : 1);
}
abelenky
  • 63,815
  • 23
  • 109
  • 159
0

Avoid multiple exit points. Avoid interlacing allocation and error handling. Follows a clean order of operation:

  1. Declare, allocate and initialize resources..
  2. If all successful, do the task.
  3. Clean-up.
  4. Return status.

 // Do all allocations first, test their `NULL`-ness, then free them all.
 int main(void) {
    // Allocate resources   
    // declare and allocate in one step
    int* i    = malloc(sizeof *i);
    double* j = malloc(sizeof *j);

    // Test for acceptability
    bool ok = i && j;

    // Perform the main body of code
    if (ok) {
      ; // do normal process in the code;
    }

    // free resources
    free(i);
    free(j);

    // return status
    return ok ? 0 : -1;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
-1
    int *i=NULL,*j=NULL;

    if(!(i=malloc(sizeof(int))))
            goto EXIT;
    if(!(j=malloc(sizeof(int))))
            goto EXIT;
    /* do some work */
    return 0;
    EXIT:
            free(i);
            free(j);
            exit(EXIT_FAILURE);

Although goto is considered a bad programming practice but here we can use it to get our task done with ease and simplicity

Mukesh
  • 83
  • 6