I'm testing my array_list with some test:
I need to check if my array list is empty after the de-allocation,
but I have some problems
...
typedef struct array_list{
void** array;
size_t size;
size_t capacity;
}array_list_t;
array_list_t* array_list_new(size_t capacity) {
array_list_t* result = (array_list_t*) malloc(sizeof(array_list_t));
result->array = (void**) malloc(sizeof(void*)*capacity);
result->size = 0;
result->capacity = capacity;
return result;
}
void array_list_free(array_list_t* array) {
free(array->array);
free(array);
}
int array_list_is_empty(array_list_t* list){
if(list->size == 0){
return 1;
}else{
return 0;
}
}
#include "unity.h"
#include "array_list.h"
...
int main () {
array_list_t* array = array_list_new(10);
TEST_ASSERT_EQUAL_INT(1, array_list_is_empty(array)); // 1 == 1 OK
array_list_insert(array,new_int(1));
TEST_ASSERT_EQUAL_INT(0, array_list_is_empty(array)); // 0 == 0 OK
array_list_free(array);
TEST_ASSERT_EQUAL_INT(1, array_list_is_empty(array)); // 1 == 0 NOT_EQUAL
}
I thought to solve this problem setting the size as 0 after free,
for example:
(... free(array); array->size = 0; array->capacity = 0; array = NULL; ...)
How do I solve this problem?
Once you do free(array) in array_list_free(), it's no longer valid to dereference array. So you need to set the array variable to NULL in main():
array_list_free(array);
array = NULL;
Then array_list_is_empty() can check whether its argument is NULL before testing the size:
int array_list_is_empty(array_list_t *list) {
return list == NULL || list->size == 0;
}
A better design would be for array_list_free() to just free array->array, and allow the caller to do free(array) when it's done with that array list. This is the usual approach: whichever component allocates an object is responsible for freeing it.
i'm testing my array_list with some test: I need to check if my array list is empty after the deallocation, but i have some problems
Yes, you have an insurmountable problem: once you free the array_list_t structure as array_list_free does, attempting to access it produces undefined behavior. The result of any test you attempt to perform on it at that point therefore does not yield any useful information (because: undefined), and the attempt might have any result whatever within the power of your C implementation, with crashing the program being an altogether plausible possibility.
I thought to solve this problem setting the size as 0 after free, for example : (... free(array); array->size = 0; array->capacity = 0; array = NULL; ...)
That's a fine alternative for clearing the list without freeing it. In particular, setting the capacity to 0 is a natural indication that the element array needs to be (re)allocated. But again, once you free the list structure itself, there's nothing more you can or should do with it. It's lifetime is over. It's kicked the bucket, it's shuffled off its mortal coil, run down the curtain and joined the bleedin' choir invisible!! THIS IS AN EX-LIST!! (Apologies to Monty Python)
you could do a=NULL; after calling array_list_free(a) to indicate that a is no-longer usable.
Or you could modify array_list_free(a) to not free a but instead set a->capacity and a->size to 0 and set a->array to NULL leaving *a in an empty looking state.
Related
I've been working on this dynamic memory allocator problem set where we must implement malloc and free and I was struggling a lot with the implementation of free. Outside of freeing the appropriate memory, there is a statistics struct that we must update with each call of malloc/free which holds a variable for the size of active allocations, active_size. To accurately update active_size the problem set says we should implement a hash table. I made the hash table to hold malloc pointers and the size of their allocations and I included a lookup_size function which would then be used in the free function to update the active_size. However, it seems the lookup_size functions is flawed and causes a seg fault at this line:
return tmp->payload;
I'll leave below the entire code as well in case anyone catches any other mistakes, but it would be a huge help if someone could figure out the cause of this seg fault. Thanks.
#define M61_DISABLE 1
#include "m61.hh"
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cinttypes>
#include <cassert>
// Hash table size
#define TABLE_SIZE 10
/// m61_malloc(sz, file, line)
/// Return a pointer to `sz` bytes of newly-allocated dynamic memory.
/// The memory is not initialized. If `sz == 0`, then m61_malloc must
/// return a unique, newly-allocated pointer value. The allocation
/// request was at location `file`:`line`.
static m61_statistics stat_count = {0, 0, 0, 0, 0, 0, 0, 4294967295};
// Hash table item defintion
typedef struct ht_item {
void* address;
unsigned long long payload;
struct ht_item* next;
} ht_item;
// Initialize hash table
ht_item* hash_table[TABLE_SIZE];
// Mod hash function
int hash_func(void* address) {
uintptr_t hash_value = (uintptr_t) address % TABLE_SIZE;
return hash_value;
}
// Empty hash table
void init_hash_table() {
for (int i = 0; i < TABLE_SIZE; i++) {
hash_table[i] = NULL;
}
}
// Hash table item insertion function
bool insert_item(ht_item* item) {
// Check if there actually is an item
if (item == NULL) {
return false;
}
// Insert item to front of the l_list (assign current val to next and new value to hash table)
int index = hash_func(item->address);
item->next = hash_table[index];
hash_table[index] = item;
return true;
}
// Hash table functiont that finds allocated sizes by mallocc
unsigned long long lookup_size(void* p) {
if (p == NULL) {
return 0;
}
int index = hash_func(p);
ht_item* tmp = hash_table[index];
while (tmp != NULL && tmp->address != p) {
tmp = tmp->next;
}
return tmp->payload;
}
void* m61_malloc(size_t sz, const char* file, long line) {
(void) file, (void) line; // avoid uninitialized variable warnings
// Your code here.
if (!base_malloc(sz)){
++stat_count.nfail;
stat_count.fail_size += sz;
}
else {
++stat_count.nactive;
++stat_count.ntotal;
stat_count.active_size += sz;
stat_count.total_size += sz;
init_hash_table();
void* p = base_malloc(sz);
ht_item* malloc_data = (ht_item*) malloc(sizeof(ht_item));
malloc_data->address = p;
malloc_data->payload = sz;
malloc_data->next = NULL;
insert_item(malloc_data);
}
return base_malloc(sz);
}
/// m61_free(ptr, file, line)
/// Free the memory space pointed to by `ptr`, which must have been
/// returned by a previous call to m61_malloc. If `ptr == NULL`,
/// does nothing. The free was called at location `file`:`line`.
void m61_free(void* ptr, const char* file, long line) {
(void) file, (void) line; // avoid uninitialized variable warnings
// Your code here.
if (ptr){
--stat_count.nactive;
stat_count.active_size -= lookup_size(ptr);
}
base_free(ptr);
}
Before getting into the answer, let me say this:
Dropping a chunk of memory management code on someone with no way to run it besides reverse-engineering the code from compiler output until it works is not nice.
Please, take a good look at this link before asking another question. https://stackoverflow.com/help/minimal-reproducible-example
So, you have this snippet of code:
while (tmp != NULL && tmp->address != p) {
tmp = tmp->next;
}
return tmp->payload;
Look at the loop condition:
while (tmp != NULL && tmp->address != p) { ... }
Assuming tmp->address never equals p, it keeps iterating as long as tmp is not NULL. In other words, it stops when tmp is NULL.
Then, in the very next line, you try to access tmp. But tmp is already NULL at that point! So you pretty much do this:
return NULL->payload;
And that's what causes the segmentation fault.
If this loop is meant to always succeed when free is called on a valid pointer, then you're probably not reliably calling insert_item in some case (in m61_malloc)?
Or, if insert_item should not be called on every call to m61_malloc, then you would not expect to find the item on every call to lookup_size, so you should check that tmp is not NULL after the loop. (you should probably do this regardless, because someone might call m61_free with an invalid pointer).
As for the cause of this bug, m61_malloc seems pretty fishy...
if (!base_malloc(sz)) {
...
} else {
...
void* p = base_malloc(sz);
...
}
return base_malloc(sz);
What do you expect this to do when the first call to base_malloc does not return NULL? As it stands, it goes into the 'else' branch, calls base_malloc a second time (either allocating again or returning a NULL that isn't handled), then calls base_malloc a third time in the return statement (again, possibly returning NULL).
In this scenario, the pointer that is stored in the hash table is the one that was returned by the second call to base_malloc, but the pointer that is returned (and that might be passed into m61_free later) is the one returned by the third call to base_malloc (which is NOT stored in the hash table).
Maybe this is the true cause of the error?
EDIT: changing m61_malloc as below fixes this particular segfault, though it's still wrong.
if (!base_malloc(sz)) {
...
return NULL;
} else {
...
void* p = base_malloc(sz);
...
return p;
}
Recently I was pondering over this question: how to make an easier way to iterate over an array of pointer in C.
If I create an array of string in C, it should look like this right?
int size = 5;
char ** strArr = (char **) malloc(sizeof(char *) * size);
if (strArr == NULL) return;
But the problem is, when you want to iterate over this array for some reason (like printing all values inside it), you have to keep track of its current size, storing in another variable.
That's not a problem, but if you create lots of arrays, you have to keep track of every single one of their sizes inside the code. If you pass this array to another function, you must pass its size as well.
void PrintValues (char ** arr, int size) {
for (int i = 0; i < size; i++)
printf("%s\n", arr[i]);
}
But when iterating over a string, it's different. You have the '\0' character, which specifies the end of the string. So, you could iterate over a string like this, with not need to keep its size value:
char * str = (char *) malloc(sizeof(char) * 4);
str[0] = 'a';
str[1] = 'b';
str[2] = 'c';
str[3] = '\0';
for (int i = 0; str[i] != '\0'; i++)
printf("%c", str[i]);
printf("\n");
Now my question:
Is it ok or morally right to allocate +1 unit in an array of pointers to maintain its tail as NULL?
char ** strArr = (char **) malloc(sizeof(char *) * (5 +1);
if (strArr == NULL) return;
strArr[0] = PseudoFunc_NewString("Car");
strArr[1] = PseudoFunc_NewString("Car#1");
strArr[2] = PseudoFunc_NewString("Car#2");
strArr[3] = PseudoFunc_NewString("Tree");
strArr[4] = PseudoFunc_NewString("Tree#1");
strArr[5] = NULL; // Stop iteration here as next element is not allocated
Then I could use the NULL pointer to control the iterator:
void PrintValues (char ** arr) {
for (int i = 0; arr[i] != NULL; i++)
printf("%s\n", arr[i]);
}
This would help me to keep the code cleaner, though it would consume more memory as a pointer size is larger than a integer size.
Also, when programming with event-based libraries, like Gtk, the size values would be released from the stack at some point, so I would have to create a pointer to dynamically store the size value for example.
In cases like this, it ok to do this? Or is it considered something bad?
Is this technique only used with char pointers because char type has a size of only 1 byte?
I miss having a foreach iterator in C...
Now my question: Is it ok or morally right to allocate +1 unit in an array of pointers to maintain its tail as NULL?
This is ok, the final NULL is called a sentinel value and using one is somewhat common practice. This is most often used when you don't even know the size of the data for some reason.
It is however, not the best solution, because you have to iterate over all the data to find the size. Solutions that store the size separately are much faster. An arrays of structs for example, containing both size and data in the same place.
Now my question: Is it ok or morally right to allocate +1 unit in an array of pointers to maintain its tail as NULL?
In C this is quite a common pattern, and it has a name. You're simply using a sentinel value.
As long as your list can not contain null pointers normally this is fine. It is a bit error-prone in general however, then again, that's C for you.
It's ok, and is a commonly used pattern.
As an alternative you can use a struct, in there you can create a size variable where you can store the current size of the array, and pass the struct as argument. The advantage is that you don't need to iterate through the entire array to know its size.
Example:
Live demo
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
char **strArr;
int size;
} MyStruct;
void PrintValues(MyStruct arr) //pass the struct as an argument
{
for (int i = 0; i < arr.size; i++) //use the size passed in the struct
printf("%s\n", arr.strArr[i]);
}
int main()
{
// using the variable to extract the size, to avoid silent errors
// also removed the cast for the same reason
char **strArr = malloc(sizeof *strArr * 5);
if (strArr == NULL) return EXIT_FAILURE;
strArr[0] = "Car";
strArr[1] = "Car#1";
strArr[2] = "Car#2";
strArr[3] = "Tree";
strArr[4] = "Tree#1";
MyStruct strt = { strArr, 5 }; // initialize the struct
PrintValues(strt); //voila
free(strArr); // don't forget to free the allacated memory
return EXIT_SUCCESS;
}
This allows for direct access to an index with error checking:
// here if the array index exists, it will be printed
// otherwise no, allows for O(1) access error free
if(arr.size > 6){
printf("%s\n", arr.strArr[6]);
}
so I have to write something like garbage collector for school project in C. I am stuck at problem with dynamic memory allocation. My function growActivePtrs() should enlarge global array of structs by one, but it does not seem to be working. I have read a ton on materials on dynamic allocation and tried many things, but I just cant find the mistake.
typedef struct activePtr
{
// pointer to actively used memory
void *memPointer;
// number of times this memory is referenced
int timesUsed;
} activePtr;
activePtr **activePointers;
size_t *ptrCount = 0;
bool growActivePtrs()
{
const size_t totalSize = *ptrCount + 1;
activePtr *temp = (activePtr *)realloc(*activePointers, (totalSize * sizeof(activePtr)));
if (temp == NULL) {
return false;
}
*activePointers = temp;
*ptrCount += 1;
activePointers[*ptrCount - 1]->timesUsed = 0;
activePointers[*ptrCount - 1]->memPointer = NULL;
return true;
}
Any help will be much appreciated. Thank you.
size_t *ptrCount = 0;
This defines ptrCount as a pointer to size_t, initialised to a null pointer. Unless you assign it the location of some actual size_t object (and it looks like you haven't), any attempt to access *ptrCount is doomed to fail.
There is no reason why this should be a pointer. You can just store a size_t directly.
As #user3121023 rightly points out in a comment, the same applies to your activePointers variable.
I have a C struct:
typedef struct {
Dataset *datasets;
int nDatasets;
char *group_name;
enum groupType type;
} DatasetGroup;
It has a constructor function like this:
DatasetGroup * new_DatasetGroup(char *group_name, enum groupType type, enum returnCode *ret)
{
DatasetGroup *dg;
dg = (DatasetGroup *) malloc(sizeof(DatasetGroup));
if (dg == NULL)
{
*ret = EMEMORY_ERROR;
}
// Allocate space for a few datasets
dg->datasets = malloc(sizeof(Dataset) * INCREMENT);
if (dg->datasets == NULL)
{
*ret = EMEMORY_ERROR;
}
dg->group_name= malloc(sizeof(char) * strlen(group_name));
strcpy(dg->group_name, group_name);
dg->type = type;
groupCount++;
return dg;
}
I want to dynamically create an array of these structs. Whats the best way to do this?
So far I have something like:
DatasetGroup * make_array(){
DatasetGroup *dg_array;
// Allocate space for a few groups
dg_array = (DatasetGroup *) malloc(sizeof(DatasetGroup) * INCREMENT);
return dg_array;
}
void add_group_to_array(DatasetGroup *dg_array, ...){
// Add a datasetgroup
DatasetGroup *dg = new_DatasetGroup(...);
// groupCount - 1 as the count is incremented when the group is created, so will always be one ahead of the array index we want to assign to
dg_array[groupCount - 1] = dg;
if (groupCount % INCREMENT == 0)
{
//Grow the array
dg_array = realloc(dg_array, sizeof(DatasetGroup) * (groupCount + INCREMENT));
}
}
But this doesnt seem right....
any ideas?
A few suggestions:
You have groupCount being incremented by the constructor function of the struct. This means you can only have one array of the struct that uses your array function. I would recommend having the array be responsible for managing the count.
To that affect if you want to have a managed array I would create a struct for that and have it keep both the pointer to the array,the number of objects and the size of the array (e.g. the maximum number of structs it can currently hold)
If you keep proper track of how many elements you have and the size of the array you can replace groupCount % INCREMENT == 0 with something like groupCount == arraySize which is a lot more intuitive in my opinion.
You can avoid the second malloc in the constructor all together by having the array be an array of the elements instead of an array of pointers. The constructor than then just initialize the struct members instead of allocating memory. If you are doing this a lot you will be avoiding a lot of memory fragmentation.
Finally, while this depends on your application, I usually recommend when you realloc do not increase by a constant but instead of by a multiple of the current array size. If say you double the array size you only have to do log_2 n number of reallocs with n being the final array size and you waste at most half of memory (memory is generally cheap, like I said it depends on the application). If that is wasting to much memory you can do say 1.5. If you want a more detailed explanation of this I recommend this Joel on Software article, the part about realloc is about 2/3 down.
Update:
A few others things:
dg = (DatasetGroup *) malloc(sizeof(DatasetGroup));
if (dg == NULL)
{
ret = EMEMORY_ERROR;
}
// Allocate space for a few datasets
dg->datasets = malloc(sizeof(Dataset) * INCREMENT);
As previously pointed out is very bad as you will us dg even if it is NULL. You probably want to exit right after detecting the error.
Furthermore you are setting ret but ret is passed by value so it will not be changed for the caller if the callee changes it. Instead you probably want to pass a pointer and dereference it.
Update 2: Can I give an example, sure, quick not so much ;-D.
Consider the following code (I apologize if there are any mistakes, still half asleep):
#include <stdio.h>
#include <stdlib.h>
#define LESS_MALLOCS
#define MAX_COUNT 100000000
typedef struct _foo_t
{
int bar1;
int bar2;
} foo_t;
void foo_init(foo_t *foo, int bar1, int bar2)
{
foo->bar1 = bar1;
foo->bar2 = bar2;
}
foo_t* new_foo(int bar1, int bar2)
{
foo_t *foo = malloc(sizeof(foo_t));
if(foo == NULL) {
return NULL;
}
foo->bar1 = bar1;
foo->bar2 = bar2;
return foo;
}
typedef struct _foo_array_t
{
#ifdef LESS_MALLOCS
foo_t *array;
#else
foo_t **array;
#endif
int count;
int length;
} foo_array_t;
void foo_array_init(foo_array_t* foo_array, int size) {
foo_array->count = 0;
#ifdef LESS_MALLOCS
foo_array->array = malloc(sizeof(foo_t) * size);
#else
foo_array->array = malloc(sizeof(foo_t*) * size);
#endif
foo_array->length = size;
}
int foo_array_add(foo_array_t* foo_array, int bar1, int bar2)
{
if(foo_array->count == foo_array->length) {
#ifdef LESS_MALLOCS
size_t new_size = sizeof(foo_t) * foo_array->length * 2;
#else
size_t new_size = sizeof(foo_t*) * foo_array->length * 2;
#endif
void* tmp = realloc(foo_array->array, new_size);
if(tmp == NULL) {
return -1;
}
foo_array->array = tmp;
foo_array->length *= 2;
}
#ifdef LESS_MALLOCS
foo_init(&(foo_array->array[foo_array->count++]), bar1, bar2);
#else
foo_array->array[foo_array->count] = new_foo(bar1, bar2);
if(foo_array->array[foo_array->count] == NULL) {
return -1;
}
foo_array->count++;
#endif
return foo_array->count;
}
int main()
{
int i;
foo_array_t foo_array;
foo_array_init(&foo_array, 20);
for(i = 0; i < MAX_COUNT; i++) {
if(foo_array_add(&foo_array, i, i+1) != (i+1)) {
fprintf(stderr, "Failed to add element %d\n", i);
return EXIT_FAILURE;
}
}
printf("Added all elements\n");
return EXIT_SUCCESS;
}
There is a struct (foo_t) with two members (bar1 and bar2) and another struct that is an array wrapper (foo_array_t). foo_array_t keeps track of the current size of the array and the number of elements in the array. It has an add element function (foo_array_add). Note that there is a foo_init and a new_foo, foo_init takes a pointer to a foo_t and new_foo does not and instead returns a pointer. So foo_init assumes the memory has been allocated in some way, heap, stack or whatever doesn't matter, while new_foo will allocate memory from the heap. There is also a preprocess macro called LESS_MALLOCS. This changes the definition of the array member of foo_array_t, the size of the initial array allocation, the size during reallocation and whether foo_init or new_foo is used. The array and its size have to change to reflect whether a pointer or the actually element is in the array. With LESS_MACRO defined the code is following my suggestion for number 4, when not, it is more similar to your code. Finally, main contains a simple micro-benchmark. The results are the following:
[missimer#asus-laptop tmp]$ gcc temp.c # Compile with LESS_MACROS defined
[missimer#asus-laptop tmp]$ time ./a.out
Added all elements
real 0m1.747s
user 0m1.384s
sys 0m0.357s
[missimer#asus-laptop tmp]$ gcc temp.c #Compile with LESS_MACROS not defined
[missimer#asus-laptop tmp]$ time ./a.out
Added all elements
real 0m9.360s
user 0m4.804s
sys 0m1.968s
Not that time is the best way to measure a benchmark but in this case I think the results speak for themselves. Also, when you allocate an array of elements instead of an array of pointers and then allocate the elements separately you reduce the number of places you have to check for errors. Of course everything has trade-offs, if for example the struct was very large and you wanted to move elements around in the array you would be doing a lot of memcpy-ing as opposed to just moving a pointer around in your approach.
Also, I would recommend against this:
dg_array = realloc(dg_array, sizeof(DatasetGroup) * (groupCount + INCREMENT));
As you lose the value of the original pointer if realloc fails and returns NULL. Also like your previous ret, you should pass a pointer instead of the value as you are not changing the value to the caller, just the callee which then exits so it has no real affect. Finally, I noticed you changed your function definition to have a pointer to ret but you need to dereference that pointer when you use it, you should be getting compiler warnings (perhaps even errors) when you do try what you currently have.
You could do two things, either you dynamically create an array of struct pointers, then call your new function to create N datagroups, or you could dynamically request memory for N structures at once, this would mean your N structures would be contiguously allocated.
Datagroup **parry = malloc(sizeof(datagroup *) * N)
for (int i = 0; i < N; i++){
parry[i] = //yourconstructor
}
Or
//allocate N empty structures
Datagroup *contarr = calloc(N, sizeof(Datagroup))
The second method might need a different initialization routine than your constructor, as the memory is already allocated
I'm working on a C project (assignment for school). One of the demands is that in case of malloc() failure, the program must free() all allocated memory and exit().
Consider a case where function A() constructs a linked-list and in each iteration it calls to another function, B(). Now, if a malloc failure occured at B(), it must free() the memory it allocated but function A() should do that as well.
Things are getting quite complicated when you have a tree of function calls larger than two.
In my previous project I used a flag to notify a malloc() failure - if a function uses another function which may use malloc(), it has to check the flag right after. It worked, but code got kinda messy.
Is there a neat solution for this problem?
Of course, with "real" applications all memory is de-allocated by the OS, but I guess this demand is pedagogical..
I think the easiest approach is to create a custom allocator (as somebody already noted in a deleted post) to keep track of all your allocations, then do a custom deallocator, use these for all your heap memory needs.
if a malloc fails you have the list of previously allocated blocks at easy reach.
e.g.
(you need to redo this cause it is not effective and should be optimized but shows the principle and only ocular compilation)
typedef struct
{
void* pMemory; /* for the allocated memory */
size_t size; /* for better debugging */
} MemoryBlock;
#define MAXBLOCKS 1000
MemoryBlock myheap[MAXBLOCKS]; // global so zero:ed
static int block = 0;
void* myalloc(size_t size)
{
static int block = 0;
// you should check vs MAXBLOCKS
myheap[block].pMemory = malloc(size);
myheap[block].size = size;
// check if it failed.
if ( myheap[block].pMemory == NULL )
{
for (int i = 0; i < block; ++i)
{
myfree(myheap[i].pMemory);
}
fprintf( stderr, "out of memory\n");
exit(EXIT_FAILURE);
}
else
{
return myheap[block++].pMemory;
}
}
void myfree(void* p)
{
for (int i = 0; i < block; ++i)
{
if ( p == myheap[i].pMemory )
{
free(myheap[i].pMemory);
myheap[i].pMemory = NULL;
return;
}
}
}
Yes. The best (and conventional) way is to initialize every pointer value to zero. Then set it during the malloc() assignment. Ex: myPtr = malloc( 10 );
It will be zero in case of failure, and you check that. And finally, when you go about freeing, you always check the pointer value before calling free():
if ( myPtr != 0 )
free( myPtr );
There is no need for an extra flag.
Are you having issue checking for errors or handling them? If you want info on catching them, use donjuedo's suggestion.
For ideas on freeing memory in the event of error, try one of these two methods:
1) For a uni-directional linked-list, keep a special pointer that points to the head of the list. In your cascading free function, start at the head, capture the next-pointer in a temp variable, free the head, move to the next structure in the list using the temp-pointer, and repeat the process until the next-pointer == 0.
2) For a bi-directional linked-list (my preference) you don't need to keep a special pointer to the head of the list. Assuming you are still at the tail, just capture the previous-pointer into a temp variable, free the tail, move back using the temp-pointer, and repeat the process until the previous-pointer == 0
You could look into the atexit() function, to register code that will be executed when the program terminates. Such code can then check if there is anything that needs to be free()d.
Note that atexit() has no way to unregister. So you need to make sure that you register each cleanup function only once, and that it does the right thing when there is nothing to clean up.
#include <stdlib.h>
#include <stdio.h>
int *ptr1;
char *ptr2;
int clean1_registered, clean2_registered;
void clean1(void)
{
printf("clean1 called\n");
if (ptr1) {
free(ptr1);
ptr1 = NULL;
}
}
void clean2(void)
{
printf("clean2 called\n");
if (ptr2) {
free(ptr2);
ptr2 = NULL;
}
}
void B(void)
{
ptr2 = malloc(100);
if (!clean2_registered) {
atexit(clean2);
}
}
void A(void)
{
ptr1 = malloc(100 * sizeof(int));
if (!clean1_registered) {
atexit(clean1);
}
B();
}
int main(int argc, char **argv)
{
A();
}