free() question

A

Aaron Walker

I apologize if this is a stupid question. I was just curious... is
there any reason why free() doesn't set the pointer (that was passed to
it) to NULL after giving the memory back to the heap? I decided to
write a wrapper for free() to do just that, and I wondered why free()
itself didn't do it.

Aaron
 
J

Joe Wright

Aaron said:
I apologize if this is a stupid question. I was just curious... is
there any reason why free() doesn't set the pointer (that was passed to
it) to NULL after giving the memory back to the heap? I decided to
write a wrapper for free() to do just that, and I wondered why free()
itself didn't do it.
There are no stupid questions. As designed, free() receives a value, the
address of the allocation to be freed. It doesn't know where the value
came from. Consider..

int *arr, *tmp;
arr = malloc(N * sizeof *arr);
tmp = arr;

...Both arr and tmp hold the address of memory returned by malloc(). It
doesn't make any difference now whether I call..

free(arr);
or
free(tmp);

...because the free() function receives the address of the allocation and
no information about arr or tmp at all. It is not possible for free() to
set them to NULL.
 
T

Thomas Lumley

Aaron Walker said:
I apologize if this is a stupid question. I was just curious... is
there any reason why free() doesn't set the pointer (that was passed to
it) to NULL after giving the memory back to the heap?

It's not a stupid question, but it *is* a FAQ (7.21)

If free() modified its argument like this it wouldn't be implementable
as a C function.

-thomas
 
K

Kevin Goodsell

Aaron said:
I apologize if this is a stupid question. I was just curious... is
there any reason why free() doesn't set the pointer (that was passed to
it) to NULL after giving the memory back to the heap? I decided to
write a wrapper for free() to do just that, and I wondered why free()
itself didn't do it.

Aaron

Some of use don't believe this would be a particularly useful feature.
If the goal is to be able to test a pointer in order to determine
whether it still points to valid accessible memory, a test against NULL
is not sufficient. Consider:

int *p1 = malloc(5 * sizeof *p1);
int *p2 = p1;
magic_free_and_NULL(p1);

if (p1 != NULL) /* OK */
{
p1[0] = 1234;
}

if (p2 != NULL) /* BAM! Undefined. */
{
p2[1] = 4321; /* BAM! Undefined. */
}

In this example, it's not even safe to test p2, let alone dereference
it. (The value of a pointer to memory that has been freed is indeterminate.)

Also, consider this:

free(p - 1);

What should be set to NULL here?

Setting freed pointers to NULL is handy in some cases, but is not good
as a general technique for determining when memory has been freed.

-Kevin
 
B

Barry Schwarz

I apologize if this is a stupid question. I was just curious... is
there any reason why free() doesn't set the pointer (that was passed to
it) to NULL after giving the memory back to the heap? I decided to
write a wrapper for free() to do just that, and I wondered why free()
itself didn't do it.
free() does not receive the pointer. C passes arguments by value.
That means that free() receives a copy of the pointer (just its
value). How would you suggest that free() trace this value back to
the variable that contains it?

This question is asked here in various guises quite frequently and is
covered in the faq. The basic answer is that a function cannot affect
the variables that contain the calling arguments.


<<Remove the del for email>>
 
M

Michael Baehr

There are no stupid questions. As designed, free() receives a value, the
address of the allocation to be freed. It doesn't know where the value
came from. Consider..

int *arr, *tmp;
arr = malloc(N * sizeof *arr);
tmp = arr;

..Both arr and tmp hold the address of memory returned by malloc(). It
doesn't make any difference now whether I call..

free(arr);
or
free(tmp);

..because the free() function receives the address of the allocation and
no information about arr or tmp at all. It is not possible for free() to
set them to NULL.

Exactly. And nothing more is necessary within the standard C library.
It would be trivial to implement one's own function which takes a handle
and sets the pointer to null, thus solving the problem without adding more
unnecessary cruft to the C library, as it appears the parent poster has
already done.

void nullfree ( void **blk )
{
free (*blk);
*blk = NULL;
}
 
T

those who know me have no need of my name

in comp.lang.c i read:
nothing more is necessary within the standard C library.

necessary -- or unnecessary?
It would be trivial to implement one's own function which takes a handle
and sets the pointer to null, thus solving the problem without adding more
unnecessary cruft to the C library, as it appears the parent poster has
already done.

void nullfree ( void **blk )
{
free (*blk);
*blk = NULL;
}

that could only assign a null pointer to the variable whose address was
passed; what of any aliases? code sloppy enough to use a pointer after it
has been free'd probably has many aliases too, yet those are not handled,
so remain armed bombs. sometimes just clearing the one pointer is enough,
and so plenty of people do have a FREE(p) macro that expands to `do {
free(p); p=0; } while (0)'. but it takes some care to keep from falling
into the trap of complacency -- which a platform that makes it clear when a
null pointer is used, e.g., by stopping the program in a very visible way
-- can help reinforce, and if it becomes ingrained any aliases are nearly
certain to bite, though perhaps not right away.
 
R

Richard Heathfield

Michael said:
It would be trivial to implement one's own function which takes a handle
and sets the pointer to null, thus solving the problem without adding more
unnecessary cruft to the C library, as it appears the parent poster has
already done.

void nullfree ( void **blk )
{
free (*blk);
*blk = NULL;
}

This is great if you happen to use void * a lot, but it is not guaranteed to
work for other kinds of pointer. For example:

int *p = malloc(n * sizeof *p);
if(p != NULL)
{
use(p, n);
nullfree(&p); /* undefined behaviour */
}

void * is a generic pointer type, but void ** is not.

You could do this:

int *p = malloc(n * sizeof *p);
if(p != NULL)
{
void *q = p;
use(p, n);
nullfree(&q);
}

but what's the point?
 
S

Simon Biber

Richard Heathfield said:
This is great if you happen to use void * a lot, but it is not
guaranteed to work for other kinds of pointer. For example:

int *p = malloc(n * sizeof *p);
if(p != NULL)
{
use(p, n);
nullfree(&p); /* undefined behaviour */

Constraint violation, in fact. However even with the cast
nullfree((void **)&p) is still undefined behaviour.
}

void * is a generic pointer type, but void ** is not.

And the reason I know that is by reading C Unleashed and the
woes over the interface for a wrapper for realloc. :)
 
R

Richard Heathfield

Simon said:
Constraint violation, in fact.

Good Lord, so it is. Thanks.
However even with the cast
nullfree((void **)&p) is still undefined behaviour.


And the reason I know that is by reading C Unleashed and the
woes over the interface for a wrapper for realloc. :)

What goes around, comes around.

(Incidentally, even in the little monologue you describe, I managed to
introduce a common error - taking the address of an rvalue!)
 
P

Peter Nilsson

Simon Biber said:
Constraint violation, in fact.

Yes, but curiously (it would seem) by 6.5.16.1p1 (assignment operators), not
by 6.5.2.2p1 (function calls).
However even with the cast
nullfree((void **)&p) is still undefined behaviour.

True, because the cast itself (or more precisely the conversion of &p, be it
explicit or implicit) potentially invokes undefined behaviour if the
resultant void** pointer is not properly aligned (6.3.2.3p5).

If the conversion _is_ aligned then the function call itself does not invoke
UB (by virtue of 6.3.2.3p5 and 6.5.16.1p2), although UB will occur at
evaluation of *blk in nullfree.
 
S

Servé Lau

Kevin Goodsell said:
int *p1 = malloc(5 * sizeof *p1);
int *p2 = p1;
magic_free_and_NULL(p1);

if (p1 != NULL) /* OK */
{
p1[0] = 1234;
}

if (p2 != NULL) /* BAM! Undefined. */

I have a vague recollection about this being undefined, but I can't imagine
a case where it would fail.
Care to explain?
 
K

Kevin Goodsell

Servé Lau said:
int *p1 = malloc(5 * sizeof *p1);
int *p2 = p1;
magic_free_and_NULL(p1);

if (p1 != NULL) /* OK */
{
p1[0] = 1234;
}

if (p2 != NULL) /* BAM! Undefined. */


I have a vague recollection about this being undefined, but I can't imagine
a case where it would fail.
Care to explain?

It will probably behave as expected on almost all implementations. But
imagine a hypothetical system with special-purpose address registers and
protected memory. Loading an address into these registers causes the
validity of the address to be checked, and some kind of exception occurs
for an invalid address. Once the memory at some address has been freed,
that address is marked invalid, so loading that address into one of the
address registers would cause an exception.

-Kevin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,125
Messages
2,570,748
Members
47,302
Latest member
MitziWragg

Latest Threads

Top