realloc zero bytes?

D

Douglas A. Gwyn

Joe Wright said:
The case for malloc(0) is unique and looks like realloc(NULL, 0). In my
view, malloc(0) should return NULL. It makes no sense to return a pointer
to zero bytes of storage. There is no case for realloc(nonnull,0) to fail.
free() does not have a failure mode.

Actually when size is specified as zero, there is explicit license
in the C standard for a conforming implementation to either
successfully allocate some amount of storage or to return a
null pointer (indicating allocation failure). The original
question cannot be resolved by taking that approach..
 
H

Hallvard B Furuseth

Douglas said:
No. realloc returns a null pointer (only) if the new object could
not be allocated, and if memory for the new object cannot be
allocated, the old objct is not deallocated and its value is unchanged.
Thats directly from the realloc spec (C99 7.20.3.4).

OK, I see it now. Though 7.20.3 says the allocation functions can
return NULL for size=0, the only text which says says realloc may free
the old object is for when it is going to return non-NULL. So this
case is opposite from C89, where realloc(nonnull, 0) freed the object.

Then a general realloc() call can be handled like this:

newobj = realloc(oldobj, size);
if (!newobj) {
#if __STDC_VERSION__ < 199901L
if (size)
#endif
free(oldobj);
return;
}
 
G

Guest

Hallvard said:
OK, I see it now. Though 7.20.3 says the allocation functions can
return NULL for size=0, the only text which says says realloc may free
the old object is for when it is going to return non-NULL. So this
case is opposite from C89, where realloc(nonnull, 0) freed the object.

Then a general realloc() call can be handled like this:

newobj = realloc(oldobj, size);
if (!newobj) {
#if __STDC_VERSION__ < 199901L
if (size)
#endif
free(oldobj);
return;
}

It seems to me that this causes massive problems for people wishing to
write libraries that conform both to C90 and to C99. Are there other
similar cases where the behaviour for a library call is defined in both
C90 and C99, but with different effects?
 
H

Hallvard B Furuseth

Harald said:
It seems to me that this causes massive problems for people wishing to
write libraries that conform both to C90 and to C99. (...)

Well, not all that massive in this case. I should have said "once you
have done realloc without checking the arguments, you can handle it like
this". It does get cumbersome if you want the assignment to be a
separate statement though.
 
H

Hallvard B Furuseth

[Reposting, previous post got out too soon]
It seems to me that this causes massive problems for people wishing to
write libraries that conform both to C90 and to C99. (...)

Well, not all that massive in this case. I should have said "once you
have done realloc without checking the arguments, you can handle it like
this". It does get cumbersome if you want the assignment to be a
separate statement though.

Otherwise:
if (!(size && (newobj = realloc(oldobj, size)))) {
free(oldobj);
return;
}
 
G

Guest

Hallvard said:
[Reposting, previous post got out too soon]
Harald said:
It seems to me that this causes massive problems for people wishing to
write libraries that conform both to C90 and to C99. (...)

Well, not all that massive in this case. [...]

Sorry that I wasn't clear; I copied too much of your post. I agree that
a program can easily be written that works with either C90 or C99's
realloc, but I was asking about a single realloc function that works
with either C90 or C99 programs.
 
J

Joe Wright

Douglas said:
Actually when size is specified as zero, there is explicit license
in the C standard for a conforming implementation to either
successfully allocate some amount of storage or to return a
null pointer (indicating allocation failure). The original
question cannot be resolved by taking that approach..
If the size argument is zero, allocation is not being attempted and I
see no rational case for a nonnull 'allocation success' return.
 
J

Joe Wright

Douglas said:
No. realloc returns a null pointer (only) if the new object could
not be allocated, and if memory for the new object cannot be
allocated, the old objct is not deallocated and its value is unchanged.
Thats directly from the realloc spec (C99 7.20.3.4).
Surely realloc(nonnull, 0) cannot allocate zero bytes. This particular
call gets wrapped inside ..

void free(void *ptr) {
realloc(ptr, 0);
}

A NULL return from realloc here is just fine.
 
C

christian.bau

Hallvard said:
Then a general realloc() call can be handled like this:

newobj = realloc(oldobj, size);
if (!newobj) {
#if __STDC_VERSION__ < 199901L
if (size)
#endif
free(oldobj);
return;
}

If newobj is not a null pointer, then realloc was successful. If newobj
is a null pointer, and size != 0, then realloc failed. If newobj is a
null pointer and size == 0, then a priori there are two possibilities:
The allocation of 0 bytes was supposed to produce a non-null pointer
and failed (oldobj is intact), or it successfully returned a null
pointer (oldobj has been free()d).

In C99, it is implementation defined whether the size == 0 case behaves
as in C90 or tries to return a non-null pointer. To make the right
decision _after_ the realloc call, you have to use your knowledge of
the implementation.

You could write
if (size == 0) { free (oldobj); newobj = malloc (0); }
else { newobj = realloc (oldobj, size); if (newobj == NULL)
handle_failure (); }
 
D

Douglas A. Gwyn

Joe said:
If the size argument is zero, allocation is not being attempted and I
see no rational case for a nonnull 'allocation success' return.

It's related to the 0-length data issue; although WG14
decided not to require support for 0-length arrays etc.,
it is undoubtedly more convenient for a programmer to have
p = malloc(n*sizeof(t));
rather than
p = n? malloc(n*sizeof(t)): NULL;
Presumably p would be used safely, since whatever loop
through array elements is coded would prevent dereferencing
p. p should have a different value than any other active
allocation, since that property is often used in programs.

Rather than require implementations to go one way or the
other on this, WG14 left it up to implementors to decide
what their users would prefer. Frankly, I think we should
have allowed 0-sized objects throughout the standard, for
reasons that we could discuss elsewhere.
 
C

CBFalconer

Joe said:
If the size argument is zero, allocation is not being attempted and I
see no rational case for a nonnull 'allocation success' return.

In all cases you alone are responsible for not using more memory
than you have allocated. If that value is 0, you obviously are not
allowed to use any of it. When the system returns a non-NULL value
for success, you can safely treat a return of NULL as an indication
of memory exhaustion (for at least the amount requested). It
simply keeps the treatment consistent, without special cases.
 
D

Douglas A. Gwyn

Joe said:
Surely realloc(nonnull, 0) cannot allocate zero bytes.

Sure it can; that is explicitly allowed by the C99 standard.
This particular call gets wrapped inside ..
void free(void *ptr) {
realloc(ptr, 0);
}
A NULL return from realloc here is just fine.

I must say I didn't understand that example. A conforming
implementation of the standard free function must *not* do
that, because either it fails to allocate and thus does not
free up the previous storage, or it successfully allocates,
frees the old storage, and the new block is lost track of,
causing a memory leak.
 
C

CBFalconer

Joe said:
Surely realloc(nonnull, 0) cannot allocate zero bytes.
This particular call gets wrapped inside ..

void free(void *ptr) {
realloc(ptr, 0);
}

A NULL return from realloc here is just fine.

No, you still can't detect memory exhaustion. A system that has to
allocate a minimum space to keep track of things may not be able
to, due to complete exhaustion. It may be too stupid to just
reduce the size of the previously allocated block, and just does an
alloc, copy, free sequence internally. The problem doesn't arise
on free, it arises on the implied malloc. I would much rather
detect the problem as early as possible.
 
C

CBFalconer

Douglas A. Gwyn said:
It's related to the 0-length data issue; although WG14
decided not to require support for 0-length arrays etc.,
it is undoubtedly more convenient for a programmer to have
p = malloc(n*sizeof(t));
rather than
p = n? malloc(n*sizeof(t)): NULL;
Presumably p would be used safely, since whatever loop
through array elements is coded would prevent dereferencing
p. p should have a different value than any other active
allocation, since that property is often used in programs.

Rather than require implementations to go one way or the
other on this, WG14 left it up to implementors to decide
what their users would prefer. Frankly, I think we should
have allowed 0-sized objects throughout the standard, for
reasons that we could discuss elsewhere.

Why do your quotes invariably seem to remove the inter-paragraph
blank lines? It makes them hard to read, and is very annoying.
 
D

David R Tribble

Joe said:
Surely realloc(nonnull, 0) cannot allocate zero bytes.

Why not? If malloc() can allocate zero bytes, why can't realloc()?

I always assumed that a malloc(0) which returned a non-null
was meant to be used to "preallocate" objects whose sizes are
not known at the time of the malloc, but still needed to be
stored as non-null pointers somewhere. The fact that the
pointers are not null provides an indication that the objects
have been preallocated. Then, presumably, later calls to
realloc() are made to complete the allocations of the objects.

Likewise, I always assumed that realloc(p,0) was meant
to "mostly" deallocate an object but keep a non-null "place
holder" for it, so that it could later either be reallocated
with a definite nonzero size, or freed completely.

As a side issue, is an implementation allowed to return
the same value (besides NULL) for all malloc(0) calls?

-drt
 
B

Ben Pfaff

David R Tribble said:
As a side issue, is an implementation allowed to return
the same value (besides NULL) for all malloc(0) calls?

No:

If the size of the space requested is zero, the behavior is
implementation-defined: either a null pointer is returned,
or the behavior is as if the size were some nonzero value,
except that the returned pointer shall not be used to access
an object.
 
D

Douglas A. Gwyn

David said:
I always assumed that a malloc(0) which returned a non-null
was meant to be used to "preallocate" objects whose sizes are
not known at the time of the malloc, but still needed to be
stored as non-null pointers somewhere. The fact that the
pointers are not null provides an indication that the objects
have been preallocated. Then, presumably, later calls to
realloc() are made to complete the allocations of the objects.

That would be one way to simplify certain algorithms.
Unfortunately, without a guarantee that malloc(0) has
to succeed (so long as storage remains available), a
portable program has to take more pains, perhaps
malloc(n?n:1)
As a side issue, is an implementation allowed to return
the same value (besides NULL) for all malloc(0) calls?

No; the spec says that at least one byte will be
actually allocated, and there is some general statement
that no two active heap allocations will overlap.
 
C

CBFalconer

Douglas A. Gwyn said:
That would be one way to simplify certain algorithms.
Unfortunately, without a guarantee that malloc(0) has
to succeed (so long as storage remains available), a
portable program has to take more pains, perhaps
malloc(n?n:1)


No; the spec says that at least one byte will be
actually allocated, and there is some general statement
that no two active heap allocations will overlap.

No.

7.20.3 p1 (n1124 or N869)

If the size of the space requested is zero, the behavior is
implementation-defined: either a null pointer is returned, or the
behavior is as if the size were some nonzero value, except that
the returned pointer shall not be used to access an object.

Your "malloc(n ? n : 1)" will have problems if a macro whenever n
is an expression.
 
D

Douglas A. Gwyn

No.
7.20.3 p1 (n1124 or N869)
If the size of the space requested is zero, the behavior is
implementation-defined: either a null pointer is returned, or the
behavior is as if the size were some nonzero value, except that
the returned pointer shall not be used to access an object.

Did you even read the previous message before responding?
Your "malloc(n ? n : 1)" will have problems if a macro whenever n
is an expression.

Obviously it was not a macro definition.
 

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,770
Messages
2,569,583
Members
45,073
Latest member
DarinCeden

Latest Threads

Top