Why not realloc(&ptr, ...) and free(&ptr)?

J

James Harris

I cannot get my head round why calls to realloc and free do not have a
pointer to the initial pointer passed to them as in the following if ptr had
been returned by malloc.

retcode = realloc(&ptr, new_size);
free(&ptr);

The idea being that if realloc could reallocate the space it would update
the pointer and return true. If it could not reallocate the space it would
leave the pointer unchanged and return false.

In the case of free() the idea is that it would set the pointer to NULL.

Anyone know why the above two calls were designed the way they were?

James
 
J

James Kuyper

I cannot get my head round why calls to realloc and free do not have a
pointer to the initial pointer passed to them as in the following if ptr had
been returned by malloc.

retcode = realloc(&ptr, new_size);
free(&ptr);

The idea being that if realloc could reallocate the space it would update
the pointer and return true. If it could not reallocate the space it would
leave the pointer unchanged and return false.

In the case of free() the idea is that it would set the pointer to NULL.

Anyone know why the above two calls were designed the way they were?

You mean like this?

char *pc = malloc(5*sizeof *pi);
long double _Complex *pd = malloc(5*sizeof *pd);
free(&pc);
free(&pd);

Do you see the problem there? pc and pd have incompatible types; those
types might have different sizes - there are real-world machines where
that is the case. Even if they were the same size, they might have
incompatible representations - though I don't know of any real-world
example of that.
Many people think that since void* can accommodate any kind of pointer
to an object type, void** can accommodate any kind of pointer to a
pointer to an object type, but C doesn't work that way. It couldn't work
that way unless it were changed to require all pointers to object types
to have the same representation and alignment. If free() were changed to
take a void**, it would have to used this way:
void * temp = pc;
free(&temp);
temp = pd;
free(&temp);
This not only makes it more complicated to use free(), but it also
eliminates the supposed advantage of this change: pc and pd are still
not null.

Even if C were changed to require all pointers to have the same
representation, so that void** could work that way, this still would
violate one of the design principles of C. You shouldn't have to pay
for the parts of C that you don't use. Well designed code often doesn't
need to waste time nulling such a pointer, because the pointer will
never be used again during the time that it would have been null. That's
true of most of the code I've written that uses free(). Why should code
that has been written that way pay the cost of having free() waste it's
time setting the pointer to null?
If you do write a lot of code that requires nulling the pointer, you can
always write a macro that wraps free(), and nulls the pointer. That way,
you get the nulling that you need, while my code still avoids wasting
time on nulling that I don't need. I'd have no great objection to adding
such a macro to the C standard library, but it's so trivial to write one
on your own that I doubt it would ever be approved.

Also, keep in mind that a nulling version of free() would not
necessarily do the entire job that needs to be done. In a typical
program where you would need to null the pointer, there's often multiple
pointers pointing into various locations inside the block of allocated
memory. They all need to be nulled, if there's a chance they'll be used
again before they've been set to a different non-null value.
 
S

Shao Miller

I cannot get my head round why calls to realloc and free do not have a
pointer to the initial pointer passed to them as in the following if ptr had
been returned by malloc.

retcode = realloc(&ptr, new_size);
free(&ptr);

The idea being that if realloc could reallocate the space it would update
the pointer and return true. If it could not reallocate the space it would
leave the pointer unchanged and return false.

In the case of free() the idea is that it would set the pointer to NULL.

Anyone know why the above two calls were designed the way they were?

What James Kuyper said. If you'd shared the declaration of 'ptr' in
your code, it might've been easier to point to. (No pun intended.)

Also consider:

void func(void) {
long * lp;

lp = malloc(sizeof *lp);
/* ... */
free(lp);
lp = NULL;
}

There's not really a need to set 'lp' to a null pointer value, here, as
its lifetime is about to expire. You might want to for security
reasons, perhaps, but then it does cost time.
 
B

Ben Bacarisse

James Harris said:
I cannot get my head round why calls to realloc and free do not have a
pointer to the initial pointer passed to them as in the following if ptr had
been returned by malloc.

retcode = realloc(&ptr, new_size);
free(&ptr);

The idea being that if realloc could reallocate the space it would update
the pointer and return true. If it could not reallocate the space it would
leave the pointer unchanged and return false.

In the case of free() the idea is that it would set the pointer to NULL.

Anyone know why the above two calls were designed the way they were?

Types have been mentioned, but there's another issue with realloc. It
has to copy the contents of the old memory to the new, but that's wasted
effort if you don't want that behaviour. For example, if it's a hash
table you will want the old elements re-hashed for the new size, not
simply copied into the old offsets. Worse, having them copied makes the
re-hashing a pain in the neck.

And when sizing down, you might want to do something to the old extra
elements (free them, for example) but that will now have to be done
before the realloc and you'll then have problems if the re-size fails.

All in all, it's simpler to re-size the memory at let the programmer
decide what needs to be done with both the old and the new allocation.
 
J

James Harris

James Kuyper said:
You mean like this?

char *pc = malloc(5*sizeof *pi);
long double _Complex *pd = malloc(5*sizeof *pd);
free(&pc);
free(&pd);

Do you see the problem there? pc and pd have incompatible types; those
types might have different sizes - there are real-world machines where
that is the case. Even if they were the same size, they might have
incompatible representations - though I don't know of any real-world
example of that.

So, in summary, pointer to type A and pointer to type B might have different
sizes and/or different representations of null?

Trying to understand the ramifications of this.....

AIUI malloc returns pointer to void. I suppose that, on machines where
needed, the compiler inserts conversions from malloc's return (which could
be null) in to a pointer to the right type. It knows the type of the
variable being assigned to so can add instructions to convert malloc's
return to a different representation if needed.

Similarly, when a program calls free() on a pointer the compiler knows that
that pointer is of type X and can perform any conversions back to the format
of a void pointer before calling free().

And when assigning NULL to a pointer or comparing a pointer with NULL the
compiler can convert length and NULL-representation as necessary.

OK. I think I've got that.

So if pointers on a given architecture were always all the same size and
always had the same representation of NULL no matter what they were pointing
at it would have been possible to call free(&p) because the free routine
could have included

*p = NULL;

But since the above cannot be guaranteed free() could not set the pointer to
null. On those odd machines there would be different NULLs.

Would it still have been OK for free to return NULL so that code could
include the following?

p = free(p);

I know that it does not return NULL. I am trying to understand the design a
bit better.

Can you remember any of the machines where pointers to different types had
different sizes? If you can I'd like to look into some more about how they
worked and which would still be in use.
Many people think that since void* can accommodate any kind of pointer
to an object type, void** can accommodate any kind of pointer to a
pointer to an object type, but C doesn't work that way. It couldn't work
that way unless it were changed to require all pointers to object types
to have the same representation and alignment. If free() were changed to
take a void**, it would have to used this way:
void * temp = pc;
free(&temp);
temp = pd;
free(&temp);
This not only makes it more complicated to use free(), but it also
eliminates the supposed advantage of this change: pc and pd are still
not null.

I don't understand this. If all pointers were the same size and represented
NULL in the same way why couldn't free(&p) work? It could both get and set
the pointer that p pointed at.

If, on the other hand, you mean the above code to be used where pointers
were not of the same size then the problem is that there's no way - sensible
or otherwise - to dereference a void * so I cannot see how the code could
work.

In terms of the design of free(), if pointers had different sizes either
free would need an extra parameter identifying the pointer type or there
would need to be different versions of free, one per pointer type - or at
least one per representation of NULL. AFAICS it's OK for mallo to return
void * because the caller knows what type conversions to carry out; but with
free() the callee would be expected to deal with the differences - something
that it could not do without knowing the pointer type.

Either way, I can see the point about machines with pointer types which
differ in size or null or both.
Even if C were changed to require all pointers to have the same
representation, so that void** could work that way, this still would
violate one of the design principles of C. You shouldn't have to pay
for the parts of C that you don't use. Well designed code often doesn't
need to waste time nulling such a pointer, because the pointer will
never be used again during the time that it would have been null. That's
true of most of the code I've written that uses free(). Why should code
that has been written that way pay the cost of having free() waste it's
time setting the pointer to null?

I'm less sure I agree with this. Clearing a pointer (p = NULL) would be
insignificant when compared with the cost of just calling the free routine
even if the free routine were to do the absolute minimum. Further, there are
many examples in the C library of distributed costs. For example, a call to
printf returns an int that's often not needed. Would you propose another set
of printf routines which did not return a value on the basis that it could
save on costs? In fact the whole suite of printf routines are probably an
order of magnitude (or more) more expensive than they need to be for most of
what they are used for, because they cope with the general case.
If you do write a lot of code that requires nulling the pointer, you can
always write a macro that wraps free(), and nulls the pointer. That way,
you get the nulling that you need, while my code still avoids wasting
time on nulling that I don't need. I'd have no great objection to adding
such a macro to the C standard library, but it's so trivial to write one
on your own that I doubt it would ever be approved.

Also, keep in mind that a nulling version of free() would not
necessarily do the entire job that needs to be done. In a typical
program where you would need to null the pointer, there's often multiple
pointers pointing into various locations inside the block of allocated
memory. They all need to be nulled, if there's a chance they'll be used
again before they've been set to a different non-null value.

Sure. That applies any time a region is realloced as well.

By the way, I take it your comments about free apply to realloc too. AFAICT
they do, i.e. realloc couldn't know what to change a pointer to on machines
where there were different sizes of pointer. Again, it could be done as
above if all pointers on a given machine were of the same size and same NULL
(nearly all machines in use today?).

Pity - it would be especially convenient for realloc where the return code
could indicat success or failure.

James
 
S

Shao Miller

So, in summary, pointer to type A and pointer to type B might have different
sizes and/or different representations of null?

Trying to understand the ramifications of this.....

AIUI malloc returns pointer to void. I suppose that, on machines where
needed, the compiler inserts conversions from malloc's return (which could
be null) in to a pointer to the right type. It knows the type of the
variable being assigned to so can add instructions to convert malloc's
return to a different representation if needed.

It's not special for 'malloc', it's a specialty of 'void *'.
Similarly, when a program calls free() on a pointer the compiler knows that
that pointer is of type X and can perform any conversions back to the format
of a void pointer before calling free().

And when assigning NULL to a pointer or comparing a pointer with NULL the
compiler can convert length and NULL-representation as necessary.

OK. I think I've got that.

So if pointers on a given architecture were always all the same size and
always had the same representation of NULL no matter what they were pointing
at it would have been possible to call free(&p) because the free routine
could have included

*p = NULL;

Why do you care about assigning the pointer to a null pointer value? Is
it because you're in a loop, or something? Security?
But since the above cannot be guaranteed free() could not set the pointer to
null. On those odd machines there would be different NULLs.

Would it still have been OK for free to return NULL so that code could
include the following?

p = free(p);

It seems to me that it's more common that nobody cares what happens to
'p' after its value has been passed to 'free'. So then it's more common
to see:

free(p);

If 'free' had a return value, then this code would ignore that return
value, and people with their compiler-warnings turned up would have to
do something like:

(void) free(p);
I know that it does not return NULL. I am trying to understand the design a
bit better.

Can you remember any of the machines where pointers to different types had
different sizes? If you can I'd like to look into some more about how they
worked and which would still be in use.


I don't understand this. If all pointers were the same size and represented
NULL in the same way why couldn't free(&p) work? It could both get and set
the pointer that p pointed at.

Beyond sizes, C cares about types. '&p' will yield a 'type-of-p *'. If
'p' is a 'long *', then '&p' will yield a 'long **'.
If, on the other hand, you mean the above code to be used where pointers
were not of the same size then the problem is that there's no way - sensible
or otherwise - to dereference a void * so I cannot see how the code could
work.

In terms of the design of free(), if pointers had different sizes either
free would need an extra parameter identifying the pointer type or there
would need to be different versions of free, one per pointer type - or at
least one per representation of NULL. AFAICS it's OK for mallo to return
void * because the caller knows what type conversions to carry out; but with
free() the callee would be expected to deal with the differences - something
that it could not do without knowing the pointer type.

Either way, I can see the point about machines with pointer types which
differ in size or null or both.


I'm less sure I agree with this. Clearing a pointer (p = NULL) would be
insignificant when compared with the cost of just calling the free routine
even if the free routine were to do the absolute minimum. Further, there are
many examples in the C library of distributed costs. For example, a call to
printf returns an int that's often not needed. Would you propose another set
of printf routines which did not return a value on the basis that it could
save on costs? In fact the whole suite of printf routines are probably an
order of magnitude (or more) more expensive than they need to be for most of
what they are used for, because they cope with the general case.

But why bother clearing the pointer? Do you initialize it? If not,
then why the asymmetric treatment?

while (cond) {
void * vp = malloc(4);
/* ...Test and do work... */
free(vp);
vp = NULL;
}

Clearing the pointer here is a waste, because it's going to be
initialized on the next iteration.
Sure. That applies any time a region is realloced as well.

By the way, I take it your comments about free apply to realloc too. AFAICT
they do, i.e. realloc couldn't know what to change a pointer to on machines
where there were different sizes of pointer. Again, it could be done as
above if all pointers on a given machine were of the same size and same NULL
(nearly all machines in use today?).

Pity - it would be especially convenient for realloc where the return code
could indicat success or failure.

'realloc' does indicate success or failure. It has one failure value
and many success values.

Something you might not be aware of: C says that a pointer's value
becomes indeterminate after that value has been passed to 'free'. So given:

void * vp1 = malloc(4);
void * vp2 = vp1;
void * vp3 = vp2;
free(vp1);

All of these pointers contain indeterminate values after the call to
'free'. This is because the lifetime of the pointed-to object has
expired, if I recall correctly.
 
K

Kenny McCormack

Shao Miller said:
Why do you care about assigning the pointer to a null pointer value? Is
it because you're in a loop, or something? Security?

The usual reason is so that the code can determine that the pointer has
been freed. I.e., it acts as a sentinel value. Among other things, this
could be used to implement parameter verification; a routine could check to
see if a passed-in-value was NULL and issue an error message like:

"p is NULL. Perhaps it has been free'd..."

....
Something you might not be aware of: C says that a pointer's value
becomes indeterminate after that value has been passed to 'free'. So given:

void * vp1 = malloc(4);
void * vp2 = vp1;
void * vp3 = vp2;
free(vp1);

All of these pointers contain indeterminate values after the call to
'free'. This is because the lifetime of the pointed-to object has
expired, if I recall correctly.

Beg to differ. I think that *de-refencing* a pointer value after it has
been free'd is, of course, UB, but the pointer itself cannot change,
because of C's call-by-value semantics.

I.e., in your example above, the value of vp1 cannot be changed as a result
of being passed to free().
 
S

Shao Miller

The usual reason is so that the code can determine that the pointer has
been freed. I.e., it acts as a sentinel value. Among other things, this
could be used to implement parameter verification; a routine could check to
see if a passed-in-value was NULL and issue an error message like:

"p is NULL. Perhaps it has been free'd..."

I see.
...

Beg to differ. I think that *de-refencing* a pointer value after it has
been free'd is, of course, UB, but the pointer itself cannot change,
because of C's call-by-value semantics.

I.e., in your example above, the value of vp1 cannot be changed as a result
of being passed to free().

Nah, C explicitly does state[6.2.4p2] that this is an exception to the
usual "callee cannot affect the caller's objects", because it's actually
affecting the set of all pointer values; which are good and which are
bad, m'kay? Doing printf("%p", vp3) after the 'free' might attempt to
load the value of 'vp3' into an address register and the CPU notices
that this does not address any storage, so it's a "mistake."
 
S

Stephen Sprunk

Beg to differ. I think that *de-refencing* a pointer value after it
has been free'd is, of course, UB, but the pointer itself cannot
change, because of C's call-by-value semantics.

The value of vp1, vp2 and vp3 does not change after calling free(), but
those existing values all become indeterminate.

For instance, merely loading an indeterminate value into an address
register can fail because that address value no longer refers to any
valid object. You don't need to dereference it to cause problems.

S
 
J

James Harris

....
Why do you care about assigning the pointer to a null pointer value? Is
it because you're in a loop, or something? Security?

A loop, no. Security, yes. Since the pointer value is no longer valid
setting it to null makes that clear to any later code. It could also be
passed to realloc which would then behave as malloc. Of course, it's
programmer responsibilty to handle any other pointers to the same area.
It seems to me that it's more common that nobody cares what happens to 'p'
after its value has been passed to 'free'. So then it's more common to
see:

free(p);

If 'free' had a return value, then this code would ignore that return
value, and people with their compiler-warnings turned up would have to do
something like:

(void) free(p);

If the compiler warned about that wouldn't it also warn about

printf("x");

Having to declare

(void) printf("x");

would be unusual.

Incidentally, I was once told casts were a bad idea (TM) as they mask other
issues. Maybe a compiler should complain about them too!

James
 
J

James Kuyper

A loop, no. Security, yes. Since the pointer value is no longer valid
setting it to null makes that clear to any later code. It could also be
passed to realloc which would then behave as malloc. Of course, it's
programmer responsibilty to handle any other pointers to the same area.

When nulling free()d pointers is necessary (and it seldom has been, in
my code), the reason for doing so is generally to allow code elsewhere
in the program to do:

if(p)
{
// *p exists
// Do things with *p
}

If the other piece of code will always know whether or not *p currently
exists, without having to check the value of p, then p doesn't need to
be nulled.

....
If the compiler warned about that wouldn't it also warn about

printf("x");

It should, for the sake of consistency.
Having to declare

(void) printf("x");

would be unusual.

Incidentally, I was once told casts were a bad idea (TM) as they mask other
issues. Maybe a compiler should complain about them too!

A cast is also often the result of someone trying to shut up a compiler
warning, who didn't understand that the cast doesn't actually fix the
problem that triggered the complaint. That's one reason why casts should
be treated with suspicion. However, it is sometimes the legitimate
purpose of a cast to disable such complaints.
 
J

James Kuyper

....
So, in summary, pointer to type A and pointer to type B might have different
sizes and/or different representations of null?

Trying to understand the ramifications of this.....

AIUI malloc returns pointer to void. I suppose that, on machines where
needed, the compiler inserts conversions from malloc's return (which could
be null) in to a pointer to the right type. It knows the type of the
variable being assigned to so can add instructions to convert malloc's
return to a different representation if needed.

Similarly, when a program calls free() on a pointer the compiler knows that
that pointer is of type X and can perform any conversions back to the format
of a void pointer before calling free().

And when assigning NULL to a pointer or comparing a pointer with NULL the
compiler can convert length and NULL-representation as necessary.

Note: be careful about how you use NULL and null. "null" is an adjective
which can, in ordinary English usage, be applied to many things, but in
C it's applied only to pointer and character values. NULL is the name of
a C macro whose expansion is required to be a null pointer constant.
There is only one NULL, #defined in several C standard headers. Any
given pointer type may or may not have multiple representations of null
pointer values; but if it has more than one, they must all compare
equal. There is a huge variety of different possible null pointer
constants, though 0 and (void*)0 are overwhelmingly the most common ones.

When used in a context where a pointer is required, null pointer
constants are automatically converted to null pointer values. This
causes many people (yourself included) to use NULL in contexts where "a
null pointer value" would have been more appropriate. However, not all
null pointer values are the immediate result of such a conversion
applied to NULL; most are not.

....
So if pointers on a given architecture were always all the same size and
always had the same representation of NULL no matter what they were pointing
at it would have been possible to call free(&p) because the free routine
could have included

*p = NULL;

Correct. To be precise, that would require a rewrite of the rules
governing compatible types, a rewrite that would be feasible because of
the "same representation" requirement.
But since the above cannot be guaranteed free() could not set the pointer to
null. On those odd machines there would be different NULLs.

Would it still have been OK for free to return NULL so that code could
include the following?

p = free(p);

Yes, if free() had been defined to return a null pointer value, that
would also have worked, and no rearrangement to the rules would be needed.
Can you remember any of the machines where pointers to different types had
different sizes? If you can I'd like to look into some more about how they
worked and which would still be in use.

I've never used such a machine, but others have mentioned them in this
forum. I never wrote down the names of those machines, just made note of
the fact that they exist (yes - present tense). Hopefully some of those
people can give you some examples.

As I understand it, the single most common reason for such pointers is
the decision to implement C on systems where the word size (the size of
the unit of memory that machine addresses refer to) is too big to be
used as the size of a C byte. On such machines, for types where
_Alignof(type) > word_size, pointers to objects of that type consist of
just a machine address for the start of the object. However, for types
such as char, where _Alignof(type)<word_size, pointers consist of a
machine address and a byte offset within the word. Depending upon how
much addressable memory a system has, having to include the byte offset
could require an increase in the pointer size.
I don't understand this. If all pointers were the same size and represented
NULL in the same way why couldn't free(&p) work? It could both get and set
the pointer that p pointed at.

In the above section, I'm describing the case where the definition of
free() were changed to take a void** argument, but the rules about
compatible types were not. Perhaps I should have been clearer about that.
If, on the other hand, you mean the above code to be used where pointers
were not of the same size then the problem is that there's no way - sensible
or otherwise - to dereference a void * so I cannot see how the code could
work.

In the context I was talking about, free() could have been defined as
follows:

void free(void**p)
{
// free the block of memory pointed at by *p

*p = NULL;
}

The only pointer that gets dereferenced has the type void**, which is
perfectly acceptable, not void*, which would be a constraint violation.
In terms of the design of free(), if pointers had different sizes either
free would need an extra parameter identifying the pointer type or there
would need to be different versions of free, one per pointer type - or at
least one per representation of NULL. ...

Those are other, more complicated ways of dealing with the issue. They
all serve to demonstrate how the way free() was actually defined was a
better choice.
I'm less sure I agree with this. Clearing a pointer (p = NULL) would be
insignificant when compared with the cost of just calling the free routine
even if the free routine were to do the absolute minimum. ...

I don't claim that it's a big cost, merely one that shouldn't be paid.

....
By the way, I take it your comments about free apply to realloc too. AFAICT
they do, i.e. realloc couldn't know what to change a pointer to on machines
where there were different sizes of pointer. Again, it could be done as
above if all pointers on a given machine were of the same size and same NULL
(nearly all machines in use today?).

Correct.
 
S

Shao Miller

If the compiler warned about that wouldn't it also warn about

printf("x");

Having to declare

(void) printf("x");

would be unusual.

Yup. People with their warnings turned way up have to worry about that
one, too.
 
K

Keith Thompson

James Harris said:
James Kuyper said:
On 08/05/2013 05:30 AM, James Harris wrote:
[...]
Would it still have been OK for free to return NULL so that code could
include the following?

p = free(p);

I know that it does not return NULL. I am trying to understand the design a
bit better.

It would have been ok, but probably not particularly useful. Having a
function always returning the same constant value seems like a bit of
wasted effort. And since it would still be perfectly possible to write

free(p);

many programmers would still do that; it doesn't *enforce* setting p to
NULL.

An alternative might have been to make free() a macro that deallocates
the memory pointed to by its argument, and then sets its argument to
NULL. Of course you can already do that:

#define FREE(p) (free(p); (p) = NULL)

but if the macro in the standard library, the function version wouldn't
have to be. But it's way too late to change that.
I don't understand this. If all pointers were the same size and represented
NULL in the same way why couldn't free(&p) work? It could both get and set
the pointer that p pointed at.

Even if all pointers have the same size and representation, they're
still of distinct types.

But if the C standard assumed that all pointers "smell alike", it could
theoretically change its type system so that void** is a generic
pointer-to-pointer type, in the same way that void* is a generic pointer
type. Then a free()-like function that takes a void** argument could
work. But that would add a requirement that the argument to free() must
be an lvalue, which is not currently the case (it commonly is anyway,
but such a requirement could break *some* code).

char *ptr malloc(42);
assert(ptr != NULL);
ptr++;
free(ptr - 1);

Historically, I suspect that free() was first implemented when C
was more weakly typed than it is now; pointers and integers could
be freely mixed, and dinosaurs roamed the earth. At the same
time, there was a strong assumption that the programmer knows what
he's doing, so there was little perceived need for a deallocation
function to set your pointers to NULL for you. ANSI added stronger
type checking, but tried not to break existing code.

But that's probably over-thinking the matter. There are several
possible ways to write a deallocation function. The author of the
library whose interface was eventually incorporated into the standard
picked a way that worked well enough.

If you're looking for a language and standard library that's a model of
coherent design, C may not be the best place to look. It is what it is.

[...]
 
K

Keith Thompson

Ben Bacarisse said:
Types have been mentioned, but there's another issue with realloc. It
has to copy the contents of the old memory to the new, but that's wasted
effort if you don't want that behaviour. For example, if it's a hash
table you will want the old elements re-hashed for the new size, not
simply copied into the old offsets. Worse, having them copied makes the
re-hashing a pain in the neck.

And when sizing down, you might want to do something to the old extra
elements (free them, for example) but that will now have to be done
before the realloc and you'll then have problems if the re-size fails.

All in all, it's simpler to re-size the memory at let the programmer
decide what needs to be done with both the old and the new allocation.

Once you've called realloc(), you can't access the old allocation.

An alternative is to malloc() the new allocation, then do whatever you
like with the old and new chunks of memory, then free() the old
allocation. But that doesn't give you realloc()'s ability to re-use the
old space.
 
K

Keith Thompson

Shao Miller said:
Nah, C explicitly does state[6.2.4p2] that this is an exception to the
usual "callee cannot affect the caller's objects", because it's actually
affecting the set of all pointer values; which are good and which are
bad, m'kay? Doing printf("%p", vp3) after the 'free' might attempt to
load the value of 'vp3' into an address register and the CPU notices
that this does not address any storage, so it's a "mistake."

I'm not sure that's necessarily an exception.

The relevant sentence is:

The value of a pointer becomes indeterminate when the object it
points to (or just past) reaches the end of its lifetime.

but I'd argue that that doesn't imply that it has a different value.
Rather it keeps the *same* value; what changes is that that value
is determinate before the call to free(), and indeterminate after.

And the same would apply to a pointer to an object with automatic
storage duration.

I think there's a DR (which I can't find at the moment) that says that
free(p) can actually change the representation of p -- which implies
that the unsigned char objects that make up its representation can have
their values change. If I'm remembering it correctly, I'm not at all
convinced that that can be derived from the normative wording of the
standard. (Can someone else find a reference to it?)
 
B

BartC

James Harris said:
I cannot get my head round why calls to realloc and free do not have a
pointer to the initial pointer passed to them as in the following if ptr
had been returned by malloc.

retcode = realloc(&ptr, new_size);
free(&ptr);

The idea being that if realloc could reallocate the space it would update
the pointer and return true. If it could not reallocate the space it would
leave the pointer unchanged and return false.

In the case of free() the idea is that it would set the pointer to NULL.

Anyone know why the above two calls were designed the way they were?

Because using pointers to pointers is more complex? (Apart from all the
type-matching issues mentioned.)

As it is now, it's fairly straightforward, and the call to realloc() matches
that to malloc(), with both returning a pointer. You can also pass an
r-value to these functions, although that has limited use.

In any case it's possible to wrap free() and realloc() with your own
versions that behave your way, I think someone mentioned.

However, zeroing one pointer probably is not so useful, as there could be a
whole bunch of other pointers into the same block. And if the ptr in &ptr
has been supplied via a function parameter, the original pointer can't be
changed.
 
S

Shao Miller

Shao Miller said:
Nah, C explicitly does state[6.2.4p2] that this is an exception to the
usual "callee cannot affect the caller's objects", because it's actually
affecting the set of all pointer values; which are good and which are
bad, m'kay? Doing printf("%p", vp3) after the 'free' might attempt to
load the value of 'vp3' into an address register and the CPU notices
that this does not address any storage, so it's a "mistake."

I'm not sure that's necessarily an exception.

The relevant sentence is:

The value of a pointer becomes indeterminate when the object it
points to (or just past) reaches the end of its lifetime.

but I'd argue that that doesn't imply that it has a different value.

I didn't mean to suggest that it changed the value's bits, but its meaning.
Rather it keeps the *same* value; what changes is that that value
is determinate before the call to free(), and indeterminate after.

And the same would apply to a pointer to an object with automatic
storage duration.

I think there's a DR (which I can't find at the moment) that says that
free(p) can actually change the representation of p -- which implies
that the unsigned char objects that make up its representation can have
their values change. If I'm remembering it correctly, I'm not at all
convinced that that can be derived from the normative wording of the
standard. (Can someone else find a reference to it?)

DR #260?:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_260.htm
 
K

Kenny McCormack

Shao Miller said:
I didn't mean to suggest that it changed the value's bits, but its meaning.

We're obviously into "angels and pins" territory here - and all of us trying
to remain catechismically correct while arguing about religious dogma.

But, that said, the following ought to be good enough to demonstrate C's
call-by-value semantics:

int x = (int) p;
free(p);
assert(x == (int) p);
 
B

Ben Bacarisse

Keith Thompson said:
Once you've called realloc(), you can't access the old allocation.

It's worse than that. I don't know what I was thinking. Consider it a
null remark -- there's nothing useful in it.

<snip>
 

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
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top