[ Obnoxious '#' quoting character fixed. ]
[ Excessively long lines re-wrapped. ]
SM Ryan <wy*****@tang o-sierra-oscar-foxtrot-tango.fake.org> writes:
ro******@ibd.nr c-cnrc.gc.ca (Walter Roberson) wrote:
Could you point me to the portion of C89 in which the interface
to realloc() is defined in a way that specifies [one way or
another] what happens to the input block when realloc() finds it
necessary to resort to the storage allocator ?
Of course it doesn't say what happens to the input block. That's the
whole point. The caller is free of concerns about the
implementation of the function, only the interface and its protocol.
My user and I would be happy to live with whatever the interface
specification -is-, but what *is* that specification?? Where is it
written in the C89 standard what exactly will happen?
The specification is the input pointer value is generally no longer valid
and should not be used again in any calls or memory references;
you must instead use the output pointer value. And that in most malloc
libraries freed memory is eventually reused.
The the input pointer remains valid if the output pointer is null and the
length is greater than zero.
Whether the input and output pointers are the same is irrelevant, because
you should only be using the output pointer if the realloc was successful.
I believe you're missing the point.
First, the original question was:
If realloc() finds it necessary to move the memory block, then
does it free() the previously allocated block?
I think the intent was to ask whether the previous block is
deallocated, not whether realloc() specifically calls the free()
function.
The prototype for realloc(), in both C90 and C99, is:
void *realloc(void *ptr, size_t size);
Here's the C90 specification of realloc() (7.10.3.4):
Description
The realloc function changes the size of the object pointed to by
ptr to the size specified by size. The contents of the object
shall be unchanged up to the lesser of the new and old sizes. If
the new size is larger, the value of the newly allocated portion
of the object is indeterminate. If ptr is a null pointer, the
realloc function behaves like the malloc function for the
specified size. Otherwise, if ptr does not match a pointer earlier
returned by the calloc, malloc, or realloc function, or if the
space has been deallocated by a call to the free or realloc
function, the behavior is undefined. If the space cannot be
allocated, the object pointed to by ptr is unchanged. If size is
zero and ptr is not a null pointer, the object it points to is
freed.
Returns
The realloc function returns either a null pointer or a pointer to
the possibly moved allocated space.
I find the statement that it "changes the size of the object"
confusing, and possibly wrong or meaningless. There's only an
indirect implication that realloc can deallocate space under some
circumstances, and nothing about what those circumstances are. We
know, based on common sense, that if realloc is asked to make an
object larger than its original size, and there isn't room to expand
it in place, it will allocate a new larger object, copy the old to the
new, deallocate the old, and return a pointer to the new -- but I see
nothing in the C90 definition that says the old object is deallocated.
Perhaps the authors of that section were so sure of the behavior that
they forgot to mention it.
Imagine a broken realloc() implementation that always allocates a new
object (copying as necessary) and returns a pointer to it, but never
deallocates the old object, either by calling free() or by some other
mechanism. A program using such an implementation would have a
serious memory leak.
I don't see how the C90 specification, quoted above, forbids such a
broken implementation.
A reasonable workaround, if you know that the implementation behaves
this way, would be to save the old pointer and explicitly call free()
on it after the realloc(). However, C90 does allow the old block to
be deallocated; if it does so, calling free() would invoke undefined
behavior.
All you can really do is cross your fingers and hope that the
realloc() implementation does deallocate the old block, even though
the standard doesn't explicitly require it to do so. As far as I
know, all real-world implementations do this; I'd be surprised to see
one that doesn't.
I think that's exactly why the wording was improved in C99 (7.20.3.4):
Description
The realloc function deallocates the old object pointed to by ptr
and returns a pointer to a new object that has the size specified
by size. The contents of the new object shall be the same as that
of the old object prior to deallocation, up to the lesser of the
new and old sizes. Any bytes in the new object beyond the size of
the old object have indeterminate values.
If ptr is a null pointer, the realloc function behaves like the
malloc function for the specified size. Otherwise, if ptr does not
match a pointer earlier returned by the calloc, malloc, or realloc
function, or if the space has been deallocated by a call to the
free or realloc function, the behavior is undefined. If memory for
the new object cannot be allocated, the old object is not
deallocated and its value is unchanged.
Returns
The realloc function returns a pointer to the new object (which
may have the same value as a pointer to the old object), or a
null pointer if the new object could not be allocated.
With this new specification, there is no potential memory leak, and
the broken implementation I described above is clearly forbidden. A
naive implementation could always allocate a new object and deallocate
the old one, but it's easy to optimize this by reusing the old object
in some cases. I don't believe the intent was to change or constrain
the behavior of realloc() relative to the C90 definition, merely to
describe it better.
So the original question was based on the flawed specification in the
C90 standard. Modular programming (which I'm sure most of us
understand perfectly well) implies that if you have a well defined
interface, you can depend on it without worrying about the internal
implementation. Nobody is disputing that. The issue here is that the
interface (C90's specification of realloc()) is not well defined.
I believe the intent is clear in both C90 and C99. A call to
realloc() that returns a pointer to a new block of memory can be
counted on to deallocate the old block *as if* by passing its address
to free(). Unfortunately, I can't prove that based on the C90
specification.
Finally, here's what the C99 Rationale has to say. (I hadn't checked
the Rationale when I wrote the above; it seems to confirm my
speculation.)
7.20.3.4 The realloc function
A null first argument is permissible. If the first argument is
not null, and the second argument is 0, then the call frees the
memory pointed to by the first argument, and a null argument may
be returned; C99 is consistent with the policy of not allowing
zero-sized objects.
A new feature of C99: the realloc function was changed to make
it clear that the pointed-to object is deallocated, a new object
is allocated, and the content of the new object is the same as
that of the old object up to the lesser of the two sizes. C89
attempted to specify that the new object was the same object as
the old object but might have a different address. This conflicts
with other parts of the Standard that assume that the address of
an object is constant during its lifetime. Also, implementations
that support an actual allocation when the size is zero do not
necessarily return a null pointer for this case. C89 appeared
to require a null return value, and the Committee felt that this
was too restrictive.
--
Keith Thompson (The_Other_Keit h)
ks***@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
We must do something. This is something. Therefore, we must do this.