views:

732

answers:

4

I am studying for a test, and I was wondering if any of these are equivalent to free(ptr):

 malloc(NULL); 

 calloc(ptr); 

 realloc(NULL, ptr); 

 calloc(ptr, 0); 

 realloc(ptr, 0);

From what I understand, none of these will work because the free() function actually tells C that the memory after ptr is available again for it to use. Sorry that this is kind of a noob question, but help would be appreciated.

+5  A: 
realloc(ptr, 0);

is equivalent to free(ptr); (although I wouldn't recommended its use as such!)

Also: these two calls are equivalent to each other (but not to free):

realloc(NULL,size)
malloc(size)
Mitch Wheat
A: 

The last one--realloc(ptr, 0)--comes close. It will free any allocated block and replace it with a minimal allocation (says my Mac OS X 10.5 manpage). Check your local manpage to see what it does on your system.

That is, if ptr pointed at a substantial object, you'll get back most of its memory.


The man page on Debian Lenny agrees with Mitch and Jonathan...does BSD really diverge from Linux on this?


From the offending man page:

The realloc() function tries to change the size of the allocation pointed to by ptr to size, and returns ptr. [...] If size is zero and ptr is not NULL, a new, minimum sized object is allocated and the original object is freed.


The linux and solaris man pages are very clean, and the '89 standard: realloc(ptr,0) works like free(ptr). The Mac OS manpage above, and the standard as quoted by Jonathan are less clear but seems to leave room to break the equivalence.

I've been wondering why the difference: the "act like free" interpretation seems very natural to me. Both of the implementations I have access to include some environment variable driven tunablity, but the BSD version accepts many more options Some examples:

 MallocGuardEdges             If set, add a guard page before and after
                              each large block.
 MallocDoNotProtectPrelude    If set, do not add a guard page before large
                              blocks, even if the MallocGuardEdges envi-
                              ronment variable is set.

 MallocDoNotProtectPostlude   If set, do not add a guard page after large
                              blocks, even if the MallocGuardEdges envi-
                              ronment variable is set.

and

 MallocPreScribble            If set, fill memory that has been allocated
                              with 0xaa bytes.  This increases the likeli-
                              hood that a program making assumptions about
                              the contents of freshly allocated memory
                              will fail.
 MallocScribble               If set, fill memory that has been deallo-
                              cated with 0x55 bytes.  This increases the
                              likelihood that a program will fail due to
                              accessing memory that is no longer allo-
                              cated.

Possibly the "minimum sized object" is nothing (i.e. equivalent to free) in the normal modes, but something with some of the guards in place. Take that for what it's worth.

dmckee
Which version of MacOS X? My (getting dated) 10.4.11 version does not say anything specific about zero size realloc(), though the statement "realloc() returns a NULL pointer if there is an error, and the allocation pointed to by ptr is still valid" implies what you sugggest. ...continued...
Jonathan Leffler
The standard, I believe , leaves it either implementation defined or undefined whether a null pointer or a pointer to some valid location of size zero (that cannot, therefore, be dereferenced safely) is returned.
Jonathan Leffler
@Jonathon: That makes sense to me, but I found this rather weird wording in man page and now I'm confused.
dmckee
Fair enough - I'm surprised too. It wouldn't be the first time BSD has gone out on a limb, and not necessarily for the worse. It basically means that my claim could be an overstatement. See also second update to my answer (about Solaris 10)
Jonathan Leffler
The man page quote is interesting; basically, it appears that the MacOS X (BSD) malloc() returns a non-null pointer for a zero-size allocation (which is allowed by the standard), and realloc() does the same. Note that the address will change, so the old memory is freed(), and you cannot use the new.
Jonathan Leffler
@Jonathan Leffler: Sorry about that. I can't seem to type your name right even when I'm looking right at it.
dmckee
+15  A: 

Actually, the last of those is equivalent to a call to free(). Read the specification of realloc() very carefully, and you will find it can allocate data anew, or change the size of an allocation (which, if the new size is larger than the old, might move the data around), and it can release memory too. In fact, you don't need the other functions; they can all be written in terms of realloc(). Not that anyone in their right mind would do so...but it could be done.

See Steve Maguire's "Writing Solid Code" for a complete dissection of the perils of the malloc() family of functions. See the ACCU web site for a complete dissection of the perils of reading "Writing Solid Code". I'm not convinced it is as bad as the reviews make it out to be - though its complete lack of a treatment of const does date it (back to the early 90s, when C89 was still new and not widely implemented in full).


D McKee's notes about MacOS X 10.5 (BSD) are interesting...

The C99 standard says:

7.20.3.3 The malloc function

Synopsis

#include <stdlib.h>
void *malloc(size_t size);

Description

The malloc function allocates space for an object whose size is specified by size and whose value is indeterminate.

Returns

The malloc function returns either a null pointer or a pointer to the allocated space.

7.20.3.4 The realloc function

Synopsis

#include <stdlib.h>
void *realloc(void *ptr, size_t size);

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.


The Solaris 10 (SPARC) man page for realloc says:

The realloc() function changes the size of the block pointer to by ptr to size bytes and returns a pointer to the (possibly moved) block. The contents will be unchanged up to the lesser of the new and old sizes. If the new size of the block requires movement of the block, the space for the previous instantiation of the block is freed. If the new size is larger, the contents of the newly allocated portion of the block are unspecified. If ptr is NULL, realloc() behaves like malloc() for the specified size. If size is 0 and ptr is not a null pointer, the space pointed to is freed.

That's a pretty explicit 'it works like free()' statement.

However, that MacOS X 10.5 or BSD says anything different reaffirms the "No-one in their right mind" part of my first paragraph.


There is, of course, the C99 Rationale...It says:

7.20.3 Memory management functions

The treatment of null pointers and zero-length allocation requests in the definition of these functions was in part guided by a desire to support this paradigm:

OBJ * p; // pointer to a variable list of OBJs
    /* initial allocation */
p = (OBJ *) calloc(0, sizeof(OBJ));
     /* ... */
     /* reallocations until size settles */
 while(1) {
    p = (OBJ *) realloc((void *)p, c * sizeof(OBJ));
         /* change value of c or break out of loop */
 }

This coding style, not necessarily endorsed by the Committee, is reported to be in widespread use.

Some implementations have returned non-null values for allocation requests of zero bytes. Although this strategy has the theoretical advantage of distinguishing between “nothing” and “zero” (an unallocated pointer vs. a pointer to zero-length space), it has the more compelling theoretical disadvantage of requiring the concept of a zero-length object. Since such objects cannot be declared, the only way they could come into existence would be through such allocation requests.

The C89 Committee decided not to accept the idea of zero-length objects. The allocation functions may therefore return a null pointer for an allocation request of zero bytes. Note that this treatment does not preclude the paradigm outlined above.

QUIET CHANGE IN C89

A program which relies on size-zero allocation requests returning a non-null pointer will behave differently.

[...]

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.


Jonathan Leffler
Specs are nice, but if the Mac OS one does what the man pages says, you'd better watch our for variation in the wild...
dmckee
Interesting. C89 explicitly says: "If size is zero and ptr is not a null pointer, the object it points to is freed." So they seem to have removed that sentence in C99?
Thomas Padron-McCarthy
+1  A: 

No. Only free will free memory.

Concerning realloc, and if it can work like free:

I interpret C99 as not saying that realloc(ptr, 0) will work like free(ptr). C89 (note: 89) explicitly says: "If size is zero and ptr is not a null pointer, the object it points to is freed." I don't have the actual C99 (note: 99) document here, but from the freely available draft documents and from the quote in Jonathan Leffler's answer, that sentence seems to have been removed in C99.

So the current C standard says nothing about realloc freeing anything. It may work that way in various implementations, and the old standard said so, but the current standard does not.

EDIT: Ok, after reading the C99 Rationale I change my mind. And after all, the C99 description of realloc (in contrast to the C89 one) actually starts with "The realloc function deallocates the old object pointed to by ptr", so I guess it's actually pretty clear when you read it.

Thomas Padron-McCarthy
See the quote from the C99 Rationale in my (updated) answer.
Jonathan Leffler