-1

I'm coding in C language for PIC32 microcontroller (there is no OS, the compiler is Microchip XC32 v1.33). I use dynamic objects (created by malloc function) but from time to time (not always) I experienced crashes. After much testing it appeared that it is connected with free-ing the memory.

I've googled it a bit and I found some comments that the order of free-ing memory is important. (LINK1, LINK2)

Could anyone explain if free-ing the dynamic memory should be in the reverse order than allocating it?

Jarek
  • 15
  • 4
  • 1
    Based on the questions you linked to I'd assume you ended up `free`ing memory and then trying to access it. – UnholySheep Dec 19 '22 at 10:21
  • 6
    The order of freeing is not directly related with the order the memory was allocated. But if you allocate memory for a struct and then more memory for pointers inside that struct, then you cannot free the pointers inside the struct after you free the struct itself. – Gerhardh Dec 19 '22 at 10:23
  • 2
    Without knowing your code, it's really hard to tell you the 'Why'. – Refugnic Eternium Dec 19 '22 at 10:23
  • 1
    I have added an answer instead of voting to close because even if the question is based on a misunderstanding (that malloc/free have to be ordered reversely of each other), that misunderstanding is in part based on answers/comments in the linked questions. Hence I think this misunderstanding needs a clear answer. – nielsen Dec 19 '22 at 11:59

2 Answers2

1

The short answer is that the order of freeing memory does not have to be the exact reverse order which the memory was malloced in.

Code like this is perfectly fine:

   int *a = malloc(sizeof(int));
   int *b = malloc(sizeof(int));
   free(a);
   free(b);

However, it may be considered good practice to free in the reverse order of allocating because it resembles a "block-begin" / "block-end" code structure where the allocated pointer is valid inside the block and invalid outside the block:

   int *a;
   a = malloc(sizeof(int));
   // *a is only defined between "malloc" and "free"
   free(a);

This is especially true if the allocated memory holds a pointer to other allocated memory as in this example:

   int **p;
   p = malloc(sizeof(int *));
   *p = malloc(sizeof(int));
   ...
   free(*p);
   free(p);

In this case, the order of the free statements cannot be changed:, but the reason is only that *p is not defined after p is freed.

Hence the following would be valid (though unusual):

   int **p;
   p = malloc(sizeof(int *));
   *p = malloc(sizeof(int));
   ...
   int *q = *p;
   free(p);
   free(q);  // free(*p) would refer to the unallocated memory (undefined behavior),
             // but the copy saved in `q` is still valid.
nielsen
  • 5,641
  • 10
  • 27
  • Thank you, but this is also not my case. Anyway, your answer shed some more light on the dynamic mem. allocation and freeing it. Thanks! – Jarek Dec 20 '22 at 08:07
  • @Jarek You are welcome. To get a more specific answer, you need to ask a more specific question. See here: https://stackoverflow.com/help/how-to-ask, especially here: https://stackoverflow.com/help/minimal-reproducible-example. Besides, in your case of microcontroller software I can only agree with Lundin that it is very likely that dynamic memory allocation is not the best option. A more common approach in embedded systems is to statically allocate objects and then, at least in the development phase, raise a clearly visible error if the allocation is insufficient. – nielsen Dec 20 '22 at 08:57
  • I'm new in writing questions in StackOverflow, so forgive me of not making myself clear enough in the question. I've edited it. You are right that without being specific, I couldn't receive a specific answer. But the answer I received was what I needed! Now I understand that in the embedded systems it is preferable to statically allocate objects. I have some dynamic objects for drawing UI components (similar approach to Microchip), and for now I will leave them as they are - they are working very well. But all other dynamic objects I will replace with the static ones. Thanks a lot! – Jarek Dec 20 '22 at 09:23
1

Lets say that you have a dynamically allocated struct and inside the struct a pointer to dynamic memory, something like this:

typedef struct
{
  item_t* items;
} note_t;

A metaphor would be stock in a warehouse. First you create a note containing the shelf number (address/pointer) where some items are stored:

note_t* note = malloc(sizeof *note);

(which could optionally also explain what those items are for, with a better variable name than note)

You'd pick a suitably large shelf depending on the number of items:

note->items = malloc( sizeof(item_t[large_enough]) );

Then you move the actual items there:

memcpy(note->items, src, sizeof(item_t[large_enough]));

Whenever you need those items, you can use that note to go to the shelf and find the items there:

note->items

// or specific item number i:
note->item[i]

Now you decide to throw away the items - they no longer fill a purpose. If you throw away the note the first thing you do, then how would you know where to find the items?

// BAD
free(note);
// note->items is now a memory leak

They will still sit there somewhere in the warehouse taking up space, but now nobody knows what they are for. Sure you could rush back and dive into the garbage dumpster and pick up the note from it, maybe it's still readable - but there are no guarantees of that. It might as well have coffee stains and old glue all over it, making it unreadable or worse - sending you to the wrong location.

Instead start by going to the shelf and removing the items and when that's done, you can safely throw away the note as well:

free(note->items);
free(note);

Metaphors aside, you should not use dynamic memory allocation on a bare metal embedded system. There is a long list of reasons why you shouldn't, and no reasons why you should. See Why should I not use dynamic memory allocation in embedded systems?

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Thank you for explaining it. The example you used is not my case, but evidently something in my code is wrong and it is connected with dynamic mem. allocation. Thank you for providing your article! That was sth new to me to be honest. I thought using dynamic allocation is a proper approach. My understanding was partly based on the fact that all widgets of Microchip Graphic Library are dynamic objects ([link - pages 8-9](https://documentation.help/Microchip-Graphics-Library/documentation.pdf). If Microchip uses heap, I understand there are use cases when it is valid... – Jarek Dec 20 '22 at 07:43
  • ... but from your article I concluded that there are many downsides of using dynamic mem. allocation. **Question:** Could it be a solution for memory fragmentation problem that at some point I could delete _all_ dynamic objects and then start building new set of objects? – Jarek Dec 20 '22 at 08:03
  • @Jarek Like pretty much all silicon vendors, Microchip has a nasty reputation when it comes to software. The rule of thumb is to never trust any code or library provided by a silicon vendor. Study it by all means and use it as a reference, but writing your own code based on the given examples is usually the way to go. – Lundin Dec 20 '22 at 08:31