??alloc(0) - std interpretation

G

Guest

7.20.3#1
[...] 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.

Then the ??alloc() functions are described in 7.20.3.* .

Does the above quote's "either, or" apply to all the ??alloc() functions
*together* or *separately*?

Example:
In GNU libc malloc(0)!=NULL, but realloc(..., 0)==NULL. Is this
compliant?

TIA
 
S

Stephen Sprunk

7.20.3#1
[...] 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.

Then the ??alloc() functions are described in 7.20.3.* .

Does the above quote's "either, or" apply to all the ??alloc() functions
*together* or *separately*?

Example:
In GNU libc malloc(0)!=NULL, but realloc(..., 0)==NULL. Is this
compliant?

realloc(ptr, 0) is defined to be equal to free(ptr) and return NULL.

Since the 'either' doesn't make sense in the case of realloc(), the logical
conclusion is that the non-NULL return is allowed for malloc() independently
of how realloc() behaves.

S
 
G

Guest

Stephen Sprunk said:
realloc(ptr, 0) is defined to be equal to free(ptr) and return NULL.

No, it's nowhere defined like that. It's not even equivalent of free()
(google for "realloc equivalent free"), although it may be implemented
this way.

In C89 there is a clause: " If size is zero and ptr is not a null
pointer, the object it points to is freed.", but this does not mean
that realloc(..., 0) merely performs free(); IMO it requires realloc()
to free the original buffer irrespective of whether subsequent zero-sized
allocation succeeds or not (in opposition to non-zero size reallocation,
where the original buffer cannot change on failure). Both C89 and C99
agree on that, only they're differently worded.

In general, not freeing realloc(..., 0) result leads to memory leak.
Since the 'either' doesn't make sense in the case of realloc(), the logical
conclusion is that the non-NULL return is allowed for malloc() independently
of how realloc() behaves.

Provided your assumption was wrong, the conclusion is irrelevant.
But my question equally might apply to malloc()/calloc() implementation.
So, can malloc(0)==NULL and calloc(0,0)!=NULL ?
 
A

Arthur J. O'Dwyer

In general, not freeing realloc(..., 0) result leads to memory leak.

Correct. Implementations are allowed to return NULL from
realloc(..., 0), in which case the free() does nothing; but they
are also allowed to return not-NULL, in which case the free() is
required.
But my question equally might apply to malloc()/calloc() implementation.
So, can malloc(0)==NULL and calloc(0,0)!=NULL ?

Sure. Do you have any reason to suspect otherwise? [In
practice, this will never happen; it's purely a QoI issue.]

-Arthur
 
G

Guest

Sure. Do you have any reason to suspect otherwise? [In

Yes, reading 7.20.3 in n869.txt, indeed I do.

All sentences in 7.20.3#1 apply equally to all ??alloc() fns. Further,
"Each such allocation shall yield a pointer to an object disjoint from
any other object." applies to the *whole group* of ??alloc() fns (ie. I
need not worry that malloc() and calloc() might overlap). I don't see why
"If the size of the space requested is zero, [...]" should have different
context; if it was meant to have, the unambiguous way to express this
would be to repeat this clause at each ??alloc() description.

BTW, I don't understand the last sentence there: "The value of a pointer
that refers to freed space is indeterminate."; where does it apply to?
practice, this will never happen; it's purely a QoI issue.]

Well, it does happen to malloc() vs. realloc(). What I mean to say,
is that no QoI shall save me any work for portable code.

The practical difference is for strict error detection. If for zero
allocation size return value may differ for each ??alloc(), then I have
to write different error-checking code for each of them.

For example:

#if MALLOC_0_NULL
# define malloc_error(ret, size) (ret == NULL && size > 0)
#else
# define malloc_error(ret, size) (ret == NULL)
#endif

#if CALLOC_0_NULL
# define calloc_error(ret, nmemb, size) (ret == NULL && nmemb > 0 && size > 0)
#else
# define calloc_error(ret, nmemb, size) (ret == NULL)
#endif

#if REALLOC_PTR_0_NULL
# define realloc_error(ret, ptr, size) \
(ptr == NULL ? malloc_error(ret, size) : ret == NULL && size > 0)
#else
# define realloc_error(ret, ptr, size) \
(ptr == NULL ? malloc_error(ret, size) : ret == NULL)
#endif

(I wrote those from scratch, hope there's no error)

MALLOC_0_NULL, CALLOC_0_NULL, REALLOC_PTR_0_NULL have to be known for
every implementation; there seems to be no "general" solution, ie.
without knowing them.
 
J

Jack Klein

7.20.3#1
[...] 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.

Then the ??alloc() functions are described in 7.20.3.* .

Does the above quote's "either, or" apply to all the ??alloc() functions
*together* or *separately*?

Example:
In GNU libc malloc(0)!=NULL, but realloc(..., 0)==NULL. Is this
compliant?

TIA

I've seen several replies and your responses in this thread, so I'll
just add three points:

1. Since the behavior is implementation-defined, you should consult
the implementation's required documentation.

2. The proper place to ask for definitive interpretation of the
wording of the standard is not here. Several members
of the standards committees read and respond there regularly.

3. N869 is not the standard, and the wording you quote (farther down
the thread, I admit) is no longer where you found it. It has moved
somewhere else. But the statement that freeing a pointer renders it
indeterminate merely provides a way of stating that any use of its
value, even if not to dereference, produces undefined behavior, as
does the use of any indeterminate value.
 
A

Arthur J. O'Dwyer

Arthur J. O'Dwyer said:
So, can malloc(0)==NULL and calloc(0,0)!=NULL ?

Sure. Do you have any reason to suspect otherwise? [In

Yes, reading 7.20.3 in n869.txt, indeed I do.

[I'm reading N869, too, BTW. I don't have a copy of the Standard.]
All sentences in 7.20.3#1 apply equally to all ??alloc() fns. Further,
"Each such allocation shall yield a pointer to an object disjoint from
any other object." applies to the *whole group* of ??alloc() fns (ie. I
need not worry that malloc() and calloc() might overlap).

Naturally.
I don't see why "If the size of the space requested is zero, [...]"
should have different context; if it was meant to have, the unambiguous
way to express this would be to repeat this clause at each ??alloc()
description.

Suppose the implementation's default behavior upon malloc(0) is to
return a newly allocated chunk of memory containing no payload -- just
a header and footer (speaking from the POV of the malloc implementor,
not the C programmer). Now suppose I write

while (1) {
if (malloc(0) == NULL) break;
}
puts("malloc returned NULL");

Do you see now that at some point 'malloc(0)' *MUST* return NULL,
no matter what it normally returns?
Since we don't know when the system will run out of free memory,
we can never say with certainty when 'malloc' (or 'calloc') will
return NULL.

BTW, I don't understand the last sentence there: "The value of a pointer
that refers to freed space is indeterminate."; where does it apply to?

free(p);
/* p is now indeterminate, and its value must not be used */
See the recent thread in Google Groups' archives if you still have
questions. This is a RAQ (Recently Asked Question). :)

practice, this will never happen; it's purely a QoI issue.]

Well, it does happen to malloc() vs. realloc(). What I mean to say,
is that no QoI shall save me any work for portable code.

I meant: If any real implementation made the following code
print "Yes", then that real implementation would be considered to
have a poor "Quality of Implementation."

#include <stdio.h>
#include <stdlib.h>
int main(void) {
if (malloc(0) != NULL) return 0;
if (calloc(0,0) == NULL) return 0;
puts("Yes");
}

As for "portability," this has little to do with it. See below.
The practical difference is for strict error detection. If for zero
allocation size return value may differ for each ??alloc(), then I have
to write different error-checking code for each of them.

For example:

#if MALLOC_0_NULL
# define malloc_error(ret, size) (ret == NULL && size > 0)
#else
# define malloc_error(ret, size) (ret == NULL)
#endif

Simply '#define malloc_error(ret,size) (ret == NULL)' and then
make sure to error-check the input to 'malloc' so that you never
pass 0 to 'malloc' to begin with. If you're dealing with legacy
code, rather than writing your own, you can always put

#define safe_malloc(b) malloc((b)+1) /* guaranteed nonzero */
#define malloc_error(p, z) (0 == (p)) /* NULL return from malloc */

and then do a search-and-replace of 'malloc' by 'safe_malloc'.
But that's a very silly way to ensure portability. It would be
much better to write sensible, non-zero-allocating code in the
first place.
MALLOC_0_NULL, CALLOC_0_NULL, REALLOC_PTR_0_NULL have to be known for
every implementation; there seems to be no "general" solution, ie.
without knowing them.

The general solution is to write code that doesn't depend on
quirks or corner cases in the implementation.

HTH,
-Arthur
 
G

Guest

Arthur J. O'Dwyer said:
[I'm reading N869, too, BTW. I don't have a copy of the Standard.]

Prompted by Jack Klein's answer, I've finally bought the C99 Standard
myself. Indeed, the wording differs slightly from n869.txt, but it
chages nothing in the discussion.


[snip]
while (1) {
if (malloc(0) == NULL) break;
}
puts("malloc returned NULL");

Do you see now that at some point 'malloc(0)' *MUST* return NULL,
no matter what it normally returns?

But that's an error return, right? All I was saying was in the case
of *successful* return.

BTW, I don't understand the last sentence there: "The value of a pointer
that refers to freed space is indeterminate."; where does it apply to?
[snip]
questions. This is a RAQ (Recently Asked Question). :)

I'm sorry. I remember even seeing that discussion in my news-reader.
Thank you for pointing this out to me, I'll read it later.

practice, this will never happen; it's purely a QoI issue.]

Well, it does happen to malloc() vs. realloc(). What I mean to say,
is that no QoI shall save me any work for portable code.

I meant: If any real implementation made the following code
print "Yes", then that real implementation would be considered to
have a poor "Quality of Implementation."
[snipped code]

Yes, I agree it would be absurd if malloc(0)!=calloc(0,0). What I wanted
to point out is that from my POV it might be considered equally absurd
that malloc(0)!=realloc(p,0). realloc(p,0) is not free(p), it "allocates"
zero-sized buffer, same way as malloc(0) does. If my interpretation
was right, then there wouldn't be such asymmetry between strict error
checking for malloc() and realloc():

/* works for all ??alloc()s */
#if ALLOC_0_NULL
# define alloc_error(ret, size) (ret != NULL && size > 0)
#else
# define alloc_error(ret, size) (ret != NULL)
#endif
Simply '#define malloc_error(ret,size) (ret == NULL)' and then
make sure to error-check the input to 'malloc' so that you never
pass 0 to 'malloc' to begin with. If you're dealing with legacy
code, rather than writing your own, you can always put

#define safe_malloc(b) malloc((b)+1) /* guaranteed nonzero */
#define malloc_error(p, z) (0 == (p)) /* NULL return from malloc */

and then do a search-and-replace of 'malloc' by 'safe_malloc'.
But that's a very silly way to ensure portability. It would be
much better to write sensible, non-zero-allocating code in the
first place.

One way or another, you have to end up writing your own wrapper library
(that does not even have to depend on the implementation!), which renders
the Standard somewhat "useless" :)

Another way is to only check for:
#define alloc_error(ret, size) (ret == NULL && size > 0)
ie. always assume malloc(0)==NULL convention. This is what I call relaxed
error checking, and will work when malloc(0)!=NULL (if the rest of the
code is portable, it must be prepared for accepting NULL and won't depend
on distinct returned pointers).

What I want for now is a portable (by that I understand correct wrt Std)
code that works with Standard Library; I want to know what the Standard
actually says, and what is deviation from it. The second motivation
is simply curiosity.

Thank you for your answers. Encouraged by Jack Klein (I thank him too)
I'm going to re-ask this question in CSC.
 

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,764
Messages
2,569,564
Members
45,040
Latest member
papereejit

Latest Threads

Top