2

If I have several linked structures in C like:

struct structA {
    int a;
    int b;
    struct structA *next;
}

struct structB {
    char a;
    int b;
    struct structB *next;
}

and I dynamically allocate memory like this:

struct structA *mystructA = (struct structA*) malloc(sizeof(struct structA));
mystructA->next = (struct structA*) malloc(sizeof(struct structA));

struct structB *mystructB = (struct structB*) malloc(sizeof(struct structB));
mystructB->next = (struct structB*) malloc(sizeof(struct structB));

do I always have to free it for each struct type like this:

struct structA *p, *pNext;
for (p = mystructA; p != NULL; p = pNext) {
    pNext = p->next;
    free(p);
}

struct structB *p, *pNext;
for (p = mystructB; p != NULL; p = pNext) {
    pNext = p->next;
    free(p);
}

or is there any generic solution? I assume there is no other solution because the free() procedure must know how many bytes have to be freed. But maybe I'm wrong and someone can teach me better.

arminb
  • 2,036
  • 3
  • 24
  • 43

3 Answers3

5

The standard way is to make the "list part" the first element of the structure, and let each derived struct share this same prefix. Since the first element is guaranteed to be placed at offset zero this wil work. Example snippet:

#include <stdlib.h>
#include <string.h>

struct list {
    struct list *next;
    };
struct structA {
    struct list list;
    int a;
    int b;
    };

struct structB {
    struct list list;
    char a;
    int b;
    };

void *create_any(size_t size) 
{
    struct list *this;
    this = malloc (size);
    if (!this) return this;
    memset(this, 0, size);
    this->next = NULL;
    return this;
}


void free_all_any(struct list **lp) {
    struct list *tmp;
    while ((tmp = *lp)) { *lp = tmp->next; free(tmp); }
}
#define CREATE_A() create_any(sizeof(struct structA))
#define CREATE_B() create_any(sizeof(struct structB))
#define FREE_A(pp) free_any((struct list **) pp)
#define FREE_B(pp) free_any((struct list **) pp)

int main(void)
{
struct structA *ap;
struct structB *bp;

ap = CREATE_A ();
bp = CREATE_B ();

// some code here ...

FREE_A( &ap);
FREE_B( &bp);

return 0;
}

This is more or less the method used in the linux kernel, but a lot more preprocessor magic is used there. (and there is no malloc there, obviously)

wildplasser
  • 43,142
  • 8
  • 66
  • 109
  • Why did you define macros and why are you calling `memset()`? – arminb Jul 19 '13 at 17:36
  • Mainly as an illustration. The macros hide the casts and the sizeof. The memset is just to set the complete object to zeros. (NULL is not necessarily represented as all-zeros, so that has to be done separately anyway) – wildplasser Jul 19 '13 at 18:19
  • Okay, everything clear now thank you! Very good generic solution but maybe little confusing on the first look (because of the macros and `memset()` :-) – arminb Jul 19 '13 at 21:49
2

Since free() accepts pointers to void * and structA and structB both have the same size, you can pass both pointer types.

This is, however, not optimal in terms of elegance. You should think about the following questions:

Why do you have two different structs with the same members?

Why do you not have a generic list item type, such as the following:

struct list_node {
    void *data;
    struct list_node *next;
}
Philip
  • 5,795
  • 3
  • 33
  • 68
  • 2
    They haven't same members and also not same size, have a closer look at my provided code. structA has 2 `int`s; structB has 1 char, 1 int – arminb Jul 19 '13 at 13:05
2

Actually, this is a very interesting question. The part is true that you have to free() each struct type individually, as they have been malloc()-ed individually, and each memory block has been allocated specifically for that type.Also, on some systems char and int have different storage sizes, but you can try a solution like Phillip provided. For more info, read about the doom memory engine. On a side note, please don't cast malloc() in C. The funny thing is that once the program is terminated, the operating system will reclaim the memory, so if you only deallocate the structures near the end of the program, when you don't need them anymore, it may not be necessary to free() them

Community
  • 1
  • 1