m0hithreddy
m0hithreddy

Reputation: 1829

Can I have a C routine, which prevents free()ing the memory twice?

Often in projects, complex structures are used, e.g., like the one below:

struct opts {
    char* server;
    char* port;
    int protocol;
    void* protocol_data;
};

And to free such structures, till today I adopted the routines as below:

void free_proto1(struct opts* opt);
void free_proto2(struct opts* opt);

void free_opts(struct opts** opt) {

    free((*opt)->server);
    free((*opt)->port);

    if ((*opt)->protocol == PROTOCOL1) {
        free_proto1(*opt);
    }
    else if ((*opt)->protocol == PROTOCOL2) {
        free_proto2(*opt);
    }
    else
        free((*opt)->protocol_data);

    free(*opt);
    *opt = NULL;
}

But If I have another struct opts pointer like struct opts* opts2 = opts1, another call to free_opts(&opt2) after calling free_opts(&opt1) would definitely result in a program crash. I know a good habit of coding is to avoid such calls. But is there by any chance, I could detect that the memory is already freed? I am interested in even looking into Process Control Block (That is where I think all the program information resides). Can I scrutinize the PCB structures, before performing a free()'ing operations, so that I can avoid free()'ing the memory twice?

Upvotes: 0

Views: 99

Answers (4)

Unfortunately, C has no support for smart pointers like f.e. C++ has.

In C, you always have to be careful to not cause memory leaks.


Anyway, You can f.e. provide another parameter in the manner of reference counting. This has the downside that you need to pass the amount of reference pointers to the allocated memory as argument everytime you call free_opts and the amount have to be fixed, but it is an approach to help you out.

The memory is only freed, if all references have been "pseudo-freed".

All passed reference pointers, except the last one, are just made a null pointer and the pointed memory is in fact not freed until the last reference pointer has been past.

int free_opts (struct opts** opt, int ref) {

    static int cnt = 0; 
    cnt++;

    if ( cnt != ref ) {        
        *opt = NULL;
        return 0;
    }

    free((*opt)->server);
    free((*opt)->port);

    if ((*opt)->protocol == PROTOCOL1) {
        free_proto1(*opt);
    }
    else if ((*opt)->protocol == PROTOCOL2) {
        free_proto2(*opt);
    }
    else
        free((*opt)->protocol_data);

    free(*opt);
    return 1;
}

Whether the memory is actually freed or not, is indicated by the return value.

Upvotes: 1

m0hithreddy
m0hithreddy

Reputation: 1829

@RobertSsupportsMonicaCellio and @Federico suggested some reliable methods of preventing the memory free'ing twice. But I feel like the suggestion made by @vll should not be lost in the comments, thus I am summarizing the poster suggestion as an answer.

Another reliable way of keeping track of the pointers is to maintain a list of the allocated addresses, and free them only when they are on the list. Below is the bare minimal implementation:

#include "list.h"

static struct list addr_list;

struct opts* create_opts(void) {

    struct opts* opts = (struct opts*) calloc(1, sizeof(struct opts));

    if (opts == NULL)
        return NULL;

    /* Initialize the opts{} */

    list_append(&addr_list, opts);

    return opts;
}

int free_opts(struct opts* opts) {

    if (in_list(&addr_list, opts) == false)
        return 0;

    /* Free the opts{} */

    list_remove(&addr_list, opts);

    return 1;
}

Of course, the above implementation simply turns down the request of freeing the structs, which are not created using create_opts(). A partial solution to that can be using flags to enable force cleaning as such. But I hope, someone if at all it is possible will come with some concrete answer.

Thanks to everyone for all the valuable suggestions :)

Upvotes: 0

Federico
Federico

Reputation: 221

You could use a reference counted API, ie: adding a size_t refs field to your struct, and then add a

struct opts* ref_opts(struct opts* opt)

API that will increase ref counter and return opt; finally, renaming free_opts to unref_opts() and only actually free your structure when refs field is 0.

This will expose a known API to take a structure reference and to free it, in a very homogeneous way; it will be users' fault if they do not use it.

Upvotes: 1

0___________
0___________

Reputation: 67476

When freeing set the pointer to the known invalid value - usually NULL.

Then even if you free more than one time - free ignores NULL pointers.

Upvotes: 0

Related Questions