Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Why not realloc(&ptr, ...) and free(&ptr)?

Reply
Thread Tools

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

 
 
James Kuyper
Guest
Posts: n/a
 
      08-05-2013
On 08/05/2013 01:29 PM, James Harris wrote:
> "Shao Miller" <(E-Mail Removed)> wrote in message
> news:ktok4a$c4v$(E-Mail Removed)...

....
>> 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.


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.

....
>> 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");


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.

 
Reply With Quote
 
 
 
 
James Kuyper
Guest
Posts: n/a
 
      08-05-2013
On 08/05/2013 11:40 AM, James Harris wrote:
> "James Kuyper" <(E-Mail Removed)> wrote in message
> news:kto16s$4kp$(E-Mail Removed)...

....
> 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.

>> 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.


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.

>> 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. ...


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.

 
Reply With Quote
 
 
 
 
Shao Miller
Guest
Posts: n/a
 
      08-05-2013
On 8/5/2013 13:29, James Harris wrote:
>
> 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.

--
- Shao Miller
--
"Thank you for the kind words; those are the kind of words I like to hear.

Cheerily," -- Richard Harter
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      08-05-2013
"James Harris" <(E-Mail Removed)> writes:
> "James Kuyper" <(E-Mail Removed)> wrote in message
> news:kto16s$4kp$(E-Mail Removed)...
>> 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.

>> 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.


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.

[...]

--
Keith Thompson (The_Other_Keith) http://www.velocityreviews.com/forums/(E-Mail Removed) <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      08-05-2013
Ben Bacarisse <(E-Mail Removed)> writes:
> "James Harris" <(E-Mail Removed)> writes:
>> 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.


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.

--
Keith Thompson (The_Other_Keith) (E-Mail Removed) <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      08-05-2013
Shao Miller <(E-Mail Removed)> writes:
[...]
> 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?)

--
Keith Thompson (The_Other_Keith) (E-Mail Removed) <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
 
Reply With Quote
 
BartC
Guest
Posts: n/a
 
      08-05-2013


"James Harris" <(E-Mail Removed)> wrote in message
news:ktnquj$6ku$(E-Mail Removed)...
> 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.

--
Bartc

 
Reply With Quote
 
Shao Miller
Guest
Posts: n/a
 
      08-05-2013
On 8/5/2013 14:39, Keith Thompson wrote:
> Shao Miller <(E-Mail Removed)> writes:
> [...]
>> 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/wg...ocs/dr_260.htm

--
- Shao Miller
--
"Thank you for the kind words; those are the kind of words I like to hear.

Cheerily," -- Richard Harter
 
Reply With Quote
 
Kenny McCormack
Guest
Posts: n/a
 
      08-05-2013
In article <ktosqr$f4$(E-Mail Removed)>,
Shao Miller <(E-Mail Removed)> wrote:
....
>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);

--
The scent of awk programmers is a lot more attractive to women than
the scent of perl programmers.

(From the "GAWK" manual)
 
Reply With Quote
 
Ben Bacarisse
Guest
Posts: n/a
 
      08-05-2013
Keith Thompson <(E-Mail Removed)> writes:

> Ben Bacarisse <(E-Mail Removed)> writes:
>> "James Harris" <(E-Mail Removed)> writes:
>>> 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.

>
> 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>
--
Ben.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
findcontrol("PlaceHolderPrice") why why why why why why why why why why why Mr. SweatyFinger ASP .Net 2 12-02-2006 03:46 PM
difference between *ptr++ and ++*ptr ? Jason C Programming 19 05-19-2005 04:50 PM
is (!ptr) or (ptr) valid way to check for NULL or NOT NULL? G Fernandes C Programming 9 02-27-2005 03:07 AM
what's the difference between delete ptr and ptr=0 -dont they accomplish the same Sid C++ 5 07-29-2004 03:42 AM
Re: realloc, need to free old ptr? Christopher Benson-Manica C Programming 4 09-01-2003 09:26 PM



Advertisments