Bj[o]rn Augestad said:
Malcolm said:
I am going to propose this function for the clc library
void *clc_wprealloc(void *buff, size_t size)
{
void *answer;
answer = realloc(buff, size);
if(!answer)
free(buff);
return answer;
}
wp stands for waterproof. What's the point of this, you might ask. The
point is that, 9 times out of 10, if system won't let you resize the
array you are working on there is no option but to free it, often
terminate the program, or else abort the function and return to a higher
level.
However, since realloc() won't free the buff it fails to resize, you need
an awkward dance with a temporary to avoid a memory leak - which probably
can't happen in reality anyway because you are asking for a few bytes to
hold characters on a system with 4GB of swap space, but you still want
the program to be correct.
clc_wprealloc() has to be part of a widely-used library like clc will
become. Adding it into user code would create dependencies and confuse
people as to why standard realloc() wasn't being called.
Hi, Malcolm, and thank you for writing.
Others have mentioned that there are many ways of recovering from an out
of memory situation. Most of these strategies does not involve freeing
the allocated memory. Since clc_wprealloc() always frees the original
memory if the call to realloc() fails, it cannot be used as a general
realloc() replacement/enhancement.
Another problem with clc_wprealloc() is that it may invoke a double
free(). If size is 0, then it is implementation defined if realloc()
returns NULL or not. (This can of course easily be fixed.
Maybe clc_wprealloc() should be renamed to something that better
describes what its intended use is and reduces the association with
realloc? clc_resize(), clc_extend(), better ideas ?
Or how about something like this little snippet, which will solve parts
of the problem with realloc()?
It doesn't.
int clc_realloc(void** p, size_t size)
{
void* np = realloc(*p, size);
if(np != NULL)
*p = np;
if(size == 0)
return 1;
else
return np != NULL;
}
The unfortunate thing is that such a solution requires casts to void**
to be able to call it without warnings, which is quite annoying in my
view. (BTW, why is such a cast needed?)
From "the literature":
"The next task is to take care of tracking all the calls to
the realloc wrapper. This function, like the other wrapper
functions, used to take void ** as its first parameter.
This was a really good idea. Instead of slavishly following
the realloc interface, I could improve upon it. After all,
it was very clear to me that the ANSI committee, or Dennis
Ritchie, or whoever it was, had not considered the design
of the realloc function as carefully as they should have
done. So, how to improve it?
I could pass the address of the old pointer, rather than
its value, and update it within the wrapper function if
and only if the internal realloc call succeeded. The
function would return not a pointer but an integer
representing success or failure, in accordance with my
normal practice at that time. Therefore, the
ptr = realloc(ptr, newsize)
bug would be squashed forever! I was really rather pleased
with this idea, and I lost little time in coding it. It
worked rather well, too.
You're right. As I discovered when recompiling and retesting
the code for this book, it's not a portable technique. While
it is true that you can assign any object pointer to a void *
pointer without loss of information, the same does not
necessarily apply to a void ** pointer! It worked fine in
the compiler I was using at the time, but that's the best that
can be said about it. In order to force it to work in ANSI C,
I'd have to cast, in the call, something like this:
DidItWork = ReallocMemory(&(void *)p, newsize);
Sometimes casting is necessary (as here), but it's always ugly.
If your function's design requires that the user must always or
nearly always cast his arguments to that function, then you have
to think seriously about whether you have the right design.
Having thought seriously about whether I had the right design,
I concluded that I didn't. I had no desire to write &(void *)
anywhere in my source code, ever. So I considered some other
possibilities. One idea was to wrap the pointer in a structure.
The trouble with that idea, though, was that I'd have to wrap
up the pointer, pass the structure, dig out the pointer within
the wrapper, do the allocation, update the pointer, and then
let the calling function dig out the pointer again, so that it
could actually be used. That sounded like a lot of hard work,
especially for the user-programmer -- something I try to avoid
if I can; I like the library to do the work whenever possible.
I was determined, though, to come up with the best possible
design, rather than just meekly copy the ANSI method.
Finally, I established a foolproof technique. Well, almost
foolproof. It would mean trusting the calling function a bit,
though. If I simply took a normal void *, I would avoid the
casting problem. Of course, the fact that C always passes
parameters by value would mean that I couldn't (effectively)
update the pointer, so I'd have to return a pointer from the
function instead of a status code. That would be all right,
though, because I could use NULL to indicate failure.
Admittedly, that would place the onus on the user-programmer
to use a spare pointer in case of failure, but that was a
small price to pay for the cleaner technique.
I took a step back to admire my handiwork and discovered
that I had just re-implemented ANSI C's realloc design! There's
a time to fight and a time to accept the inevitable, so I gave
in; it seems that whoever designed realloc did know what they
were doing, after all. There's a lesson there somewhere for
overzealous and arrogant wheel inventors."