0

I am learning C and I can't free a dynamically allocated array of structs. Here is my sample code:

typedef char Str50[50];

typedef struct exam {
    Str50 firstname;
    Str50 lastname;
    Str50 className;
    int score;
    Str50 date;
} Exam;

Exam *examDB

size_t allocateAndFreeTheStructArray {

    size_t numRecs = 100; //let's say the array contains 100 elements
    examDB = malloc(numRecs * sizeof * examDB); //this allocates the needed memory

    // the code below tries to free what malloc allocated
    for (size_t i = 0; i < numRecs; i++) {
        free(&examDB[i]);
    }
    free(examDB);
}

When I compile this for windows, everything works fine (it seems). When I compile the same piece of code on my mac using xcode, I get an error that says: "malloc: *** error for object 0x7fae610060d0: pointer being freed was not allocated"

How is that possible? I used malloc to create the array... What am I missing here?

Fred Larson
  • 60,987
  • 18
  • 112
  • 174

4 Answers4

4

You allocated a flat array of 100 objects. You're then trying to free each object separately and the list at the end - that's a pattern used when you have an array of pointers to objects. In your case, you can't free just one object in a flat array.

You have two options. Keep the flat array and just call free once:

size_t numRecs = 100; 
examDB = malloc(numRecs * sizeof * examDB); 

free(examDB);

Or change examDB to a pointer to pointer to Exam to keep a list of pointers:

Exam **examDB;

and then this would be the valid code:

size_t numRecs = 100; 
examDB = malloc(numRecs * sizeof * examDB); 

for (size_t i = 0; i < numRecs; i++) {
    examDB[i] = malloc(sizeof ** examDB);
}

for (size_t i = 0; i < numRecs; i++) {
    free(&examDB[i]);
}
free(examDB);

BTW, this must be one of the most confusing malloc invocations I've ever seen:

malloc(numRecs * sizeof * examDB)
krzaq
  • 16,240
  • 4
  • 46
  • 61
2

You need a free for each malloc. How many mallocs you have? 1, so how many free you need? Just 1.

You are allocating an array of 100 * sizeof(examDB) elements, which is dynamic but each element is a examDB, not a pointer to an examDB so you just need to free the whole array.

You would have required to free each element of the array if you had something like

// 1 malloc
examDB** array = malloc(sizeof(examDB*) * 100);

// 100 malloc
for (int i = 0; i < 100; ++i)
  examDB[i] = malloc(sizeof(examDB));

...

// 100 free
for (int i = 0; i < 100; ++i)
  free(examDB[i]);

// 1 free
free(examDB);
Jack
  • 131,802
  • 30
  • 241
  • 343
2

You allocated an array of objects. You can't free them by elements. What you can do is just free examDB itself:

 free(examDB);

The reason to do this is, as I said before, you are allocating a whole array of null values, as this code you posted states:

examDB = malloc(numRecs * sizeof * examDB); 

What you can do to free element by element is to initialize your allocated elements like this:

for (int i = 0; i < 100; ++i)
  examDB[i] = malloc(sizeof(examDB));

Here is a C++ reference for malloc.

Tip: I have never seen this malloc invoked like this:

malloc(numRecs * sizeof * examDB); 

It's more common to put it this way:

malloc(numRecs * sizeof(examDB)); 
zondo
  • 19,901
  • 8
  • 44
  • 83
amanuel2
  • 4,508
  • 4
  • 36
  • 67
0
examDB = malloc(numRecs * sizeof * examDB); //this allocates the needed memory

This expression allocates a single contiguous block of numRecs * sizeof *examDB, and sizeof *examDB is the same as sizeof struct exam. So at this point you can use examDB much in the same way as if it was declared struct exam examDB[numRecs].

Calls to free should be paired with calls to malloc and since you only malloc once, you only need to free once.

 examDB = malloc(numRecs * sizeof * examDB); //this allocates the needed memory
 //...
 free(examDB);
kfsone
  • 23,617
  • 2
  • 42
  • 74