Ratatouille
Ratatouille

Reputation: 1492

Fail to understand why freeing memory throws an error

I was reading to Dangling Pointer and found it's a good habit do this, to prevent oneself from dangling pointer error.

free(ptr); // free the ptr
ptr = NULL; 

Now I decided to test this with a sample vanilla C code.

CASE_1

char *ptr = malloc(10);
... 
...
free(ptr); 
ptr=NULL;
// Just to check what happen if I call free more than I once
free(ptr)
ptr=NULL;

All work fine. Until I decided to wrap the free and the pointer NULL assignment in a function I named it safefree

void safefree(char *pp) {
  free(pp);
  pp = NULL;
}

CASE_2

Now, when I ran the above method more than 1 (like this)

safefree(ptr);

safefree(ptr);

I get the following error.

malloc: *** error for object 0x7fd98f402910: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug

I happen to understand the error (pointer being freed was not allocated) but I fail to understand why it doesn't fail in the CASE_1 and fails in latter part of the sample code.

Upvotes: 1

Views: 442

Answers (2)

Sourav Ghosh
Sourav Ghosh

Reputation: 134396

First, let's review the behaviour of free(). Quoting C11, chapter §7.22.3.3

void free(void *ptr);

The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs. Otherwise, if the argument does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined.

Follow the two emphasized points.

  • You can pass a null pointer, NULL, to free() as many times as you want, they are valid calls (just to be ignored).
  • You cannot pass a pointer which has already been passed to free() once.

Case 1:

Here, after calling free() with the pointer, we're explicitly setting the pointer to NULL. That is why, calling free() with the same pointer variable later, any number of time, is not an issue, at all.

case 2:

C uses pass-by-value for function argument passing. That's why, When wrapped inside a function,

free(pp);

works as expected, (it passes the required pointer to free()) but

pp = NULL;

is local to the function and that change is not reflected to the caller. Thereby, calling the function again causes double free, as now,

  • the pointer has already been passed to free()
  • the assigned NULL is not reflected to the caller, hence the pointer is not set to NULL
  • in the next call, we're passing already-free()d pointer again.

As already mentioned, this invokes undefined behavior.

Solution: You need to pass a pointer to the pointer as the argument of the called function safefree() and from the called function, you can set the pointer value to NULL to get it reflected in the caller. Something like

void safefree(void ** ptrToPtr) 
{ 
     free(*ptrToPtr); 
     *ptrToPtr= NULL; 
 }

and calling like

 safefree (&ptrToBeFreed);

will do the job (mind the types there, anyways).

Upvotes: 5

Aganju
Aganju

Reputation: 6405

Aside from Jonathan's remark, if you do something that results in undefined behavior (free the pointer again), it results in undefined behavior (errors, crashes).

Doing the same thing in different contexts does not necessarily result in the same undefined behavior, it depends on implementation details; they are typically only known to the compiler developer, so they seem 'random' from the outside. Nothing much can be learned by analyzing random undefined behavior. It could for example be that the compile time affects the result..., or the spelling of the directory you work in... anything.

Upvotes: 1

Related Questions