what malloc(0) should returns?

C

CBFalconer

Yevgen said:
But p2 could also be changed. E.g. malloc and free could be

Please don't send email copies of usenet articles. It is just
further clutter and annoyance at this end.
 
O

Old Wolf

Richard said:
It's stricter than that. It's not just implementation-dependent, but
implementation-defined. That means that the implementation's documentation
must tell you which choice it made.

For example, Microsoft's C compiler documentation says:

The calloc, malloc, and realloc functions accept zero as an argument. No
actual memory is allocated, but a valid pointer is returned and the memory
block can be modified later by realloc.

Well, the implementation is allowed to return NULL if allocation
failed. So, effectively we have that this implementation may either
return a valid pointer or return NULL in case of malloc(0).

You might say that allocation cannot fail when zero bytes are
requested. However, the standard also says that the pointer
returned should point to the first byte of allocated storage.
So it appears that allocation is required, and therefore, it may fail.
 
S

Serve Laurijssen

CBFalconer said:
Not so. The standard requires malloced pointers to objects be
distinct. After the free(p) p is no longer a pointer to an object.

I think keith missed the free because he says later "assuming there are no
intervening calls to free()"
 
K

Keith Thompson

And I was wrong.
Er, I suspect this would break a lot of code...

It would break a lot of implementations, but it shouldn't break much
code; well-written code shouldn't *care* about the values of free()d
pointers.
...I believe you may have misspelled "No, I'm not" up above.
Would you mind clarifying your clarification?

I missed the free() call. Certainly an implementation can re-use
free()d addresses.

What I *meant* to assert is that, for example, the following:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *p1 = malloc(0);
void *p2 = malloc(0);
if (p1 == NULL || p2 == NULL || p1 != p2) {
puts("ok");
}
else {
puts("oops");
}
return 0;
}

must always print "ok".

Note that if malloc(0) returns a null pointer, it can be either
because the implementation always does that, or because there's not
enough available memory.

In my opinion, the behavior defined by the standard for malloc(0) is
not particularly useful.
 
K

Keith Thompson

CBFalconer said:
Not so. The standard requires malloced pointers to objects be
distinct. After the free(p) p is no longer a pointer to an object.

Quite correct; I missed the call to free().
 
S

Serve Laurijssen

jaysome said:
On Mon, 18 Dec 2006 16:29:02 +0000, Richard Heathfield
The last sentence in the above quote is meant for your own good, and
has nothing to do with how the OS might misbehave (though it could).
All WIN32 platforms--95/98/ME/NT/2K/XP/2003/Vista/32/64--gracefully
handle null pointer dereferences.

depends if you call access violations graceful ;)
 
R

Random832

2006-12-19 said:
In the sense that it behaves that way. However you can't legally
dereference it, just as you can't alter an anonymous char string.

To what object does it point?
 
G

Guest

Random832 said:
To what object does it point?

It doesn't have a name, just as the result of malloc(sizeof(int))
doesn't have a name.

An object is defined as "region of data storage in the execution
environment, the contents of which can represent values". Since
malloc(0) must (if it doesn't return NULL) behave as if a positive
argument is passed, except for dereferencing, you still get a pointer
to a region. In particular, if you convert malloc's result to a
character pointer, it means you're allowed to increment it at least
once. The fact that you're not allowed to access the value is
irrelevant, since the definition doesn't state otherwise.
 
C

CBFalconer

Harald said:
It doesn't have a name, just as the result of malloc(sizeof(int))
doesn't have a name.

An object is defined as "region of data storage in the execution
environment, the contents of which can represent values". Since
malloc(0) must (if it doesn't return NULL) behave as if a positive
argument is passed, except for dereferencing, you still get a pointer
to a region. In particular, if you convert malloc's result to a
character pointer, it means you're allowed to increment it at least
once. The fact that you're not allowed to access the value is
irrelevant, since the definition doesn't state otherwise.

Since it points to zero storage bytes, it has already been
incremented to one past the storage area.
 
S

Simon Biber

Harald said:
An object is defined as "region of data storage in the execution
environment, the contents of which can represent values". Since
malloc(0) must (if it doesn't return NULL) behave as if a positive
argument is passed, except for dereferencing, you still get a pointer
to a region. In particular, if you convert malloc's result to a
character pointer, it means you're allowed to increment it at least
once. The fact that you're not allowed to access the value is
irrelevant, since the definition doesn't state otherwise.

Are you sure that you're allowed to increment it? I think that's unclear
from the C standard's wording:

"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."

It does not merely say that the returned pointer shall not be
dereferenced. It says that it shall not be used to access an object. If
you convert the pointer to char* and increment it, that action is only
valid if there is an object of at least one byte at the address. So it
could be argued that in effect you have used the pointer to access an
object.
 
B

Ben Pfaff

CBFalconer said:
Since it points to zero storage bytes, it has already been
incremented to one past the storage area.

No, it points to at least one byte. It's just that that byte, or
those bytes, cannot be accessed. The standard is quite clear
about this:

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.

I, at least, don't think that incrementing a pointer is
"access[ing] an object". Do you?
 
B

Ben Pfaff

Simon Biber said:
If you convert the pointer to char* and increment it, that
action is only valid if there is an object of at least one byte at the
address. So it could be argued that in effect you have used the
pointer to access an object.

The standard defines "access" like this:

3.1
1 access
execution-time action to read or modify the value of an object

Incrementing a pointer does not read or modify the value of the
object that the pointer points to (unless the pointer points to
itself, but that's not the case here).
 
S

Simon Biber

Richard said:
Chris Dollin said:
If an implementation chose to return the same pointer P for each malloc(0)
then realloc would need to know that P was operationally equal
to null, and hence in your code above the realloc would just mallocate
100 bytes for your convenience.

Ok, now try this one:

char *p1, *p2;
p1=malloc(0); p2=malloc(0);
p1=realloc(p1, 200);
p2=realloc(p2, 100);
p1[123]='?';

This code is fine, so long as the realloc on the third line succeeds,
otherwise the fifth line dereferences a null pointer.

If we're on an implementation that returns the same
pointer P for each malloc(0):
line 1: defines p1 and p2
line 2: p1 and p2 both gets the 'special' pointer P
line 3: realloc detects the P and returns malloc(200)
line 4: realloc detects the P and returns malloc(100)
line 5: the 124th byte of the block allocated on line 3 is set to '?'

If subsequent (non-free()d) calls to malloc(0) were allowed to deliver
identical pointers, that code would be allowed to make flying reindeer
come out of the North Pole.

Absolutely not. There is no undefined behaviour. The third line modifies
the value of p1 but it does not modify the value of p2. The value of p2
may still have the special pointer P, so the fourth line behaves exactly
like the third line did.

But Santa is just a cheap copy of the real,
Dutch, Saint Nicholas, and that code (bad use of realloc() excepted)
does not have undefined behaviour. Therefore, all pointers returned from
malloc(0) must be either null or not equal to any other pointer,
including ones obtained from other calls to malloc(0).

Faulty logic. The code has no undefined behaviour even if malloc(0) does
return the same pointer each time.
 
S

Simon Biber

Chris said:
Since malloc() is a reserved function name, let me illustrate with
a different function-name. This also lets me use malloc() for the
"hard parts".

#include <stdlib.h>

static char magic;

void *nalloc(size_t size) {
if (size == 0)
return &magic; /* or: return NULL */
return malloc(size);
}

void nfree(void *p) {
if (p != &magic)
free(p);
}

void nrealloc(void *p, size_t size) {
if (p == &magic)
p = NULL;
if (size == 0) {
free(p);
return &magic; /* or: return NULL */
}
return realloc(p, size);
}

Looks good, except that nrealloc should return void *.
Given the obvious preconditions, the behavior of this code is
well-defined (which may suffice to label it "sensible"), but would
it suffice to replicate the Standard's requirements for malloc()?

I believe the answer is "no":

/* insert appropriate #include lines, etc., as needed */

#define Xalloc nalloc /* or malloc */
#define NAME "nalloc" /* or "malloc" */
#define Xfree nfree /* or free */

void check_it(void) {
void *p1 = Xalloc(0);
void *p2 = Xalloc(0);

if (p1 == NULL && p2 == NULL)
puts("OK: " NAME "(0) returned NULL each time");
else if (p1 == p2)
puts("FAIL: p1 == p2, but p1 and p2 are not NULL");
else {
puts("OK: alloc(0), called twice, returned two different")
puts("non-NULLs, or one non-NULL and one NULL");
}
Xfree(p1);
Xfree(p2);
}
>
If we use this check_it() routine on nalloc(), it will print the
"FAIL" line, because p1 and p2 are equal, yet at least one of the
two pointers is not NULL (in fact both point to the "magic" byte).

Yes. Though if there weren't the explicit restriction in the standard:
"Each such allocation shall yield a pointer to an object disjoint
from any other object."
Then this behaviour of nalloc would be acceptable as a replacement for
malloc.
 
S

Stephen Sprunk

Keith Thompson said:
In my opinion, the behavior defined by the standard for malloc(0) is
not particularly useful.

As with many C oddities, it was more an issue of documenting what
existing pre-ANSI C implementations did, not specifying ideal behavior.

A lot of things in C would be very different if it had been specified
fully before implementations were written -- but it never would have
caught on.

S
 
S

Stephen Sprunk

Random832 said:
To what object does it point?

To the zero-byte object that malloc() allocated :)

Of course, asking for details about a zero-byte object is pointless,
since an object of zero bytes has no properties other than an address.
It's like asking about properties of a object of type void (and I don't
mean void*).

Harald's comment about being able to increment a pointer to a zero-byte
object is interesting, though. Should that be legal? IMHO, the pointer
already points to the byte after the end of the object when you get it,
and it'd be UB to try to increment it -- or decrement it.

S
 
R

Random832

2006-12-19 said:
Looks good, except that nrealloc should return void *.


Yes. Though if there weren't the explicit restriction in the standard:
"Each such allocation shall yield a pointer to an object disjoint
from any other object."

All zero-length objects are disjoint from one another. Or at least as
many as the number of angels that can dance on the head of a pin.
 
W

WaterWalk

Keith said:
*sniped* <

A quick test of a few systems I happen to have immediate access to
shows that all of them either return a null pointer, or return
distinct values on two successive calls to malloc(0).

As for why this is required, C99 7.20.3p1 says:

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.

One aspect of the behavior of malloc() with a non-zero size is that
successive calls return unique values. My interpretation is that this
same behavior is required for non-null results of malloc(0).

The simplest way to implement this is to quietly translate "malloc(0)"
to "malloc(1)".

Does this imply that if an implementation makes malloc(0) return a
non-null pointer, this pointer shall be free()d?
 
C

CBFalconer

WaterWalk said:
Does this imply that if an implementation makes malloc(0) return
a non-null pointer, this pointer shall be free()d?

Yes. Implied by the fact that the malloc subsystem has to keep
track of allocations somewhere, and this necessarily consumes some
sort of storage. free then releases any such.
 
K

Keith Thompson

Stephen Sprunk said:
To the zero-byte object that malloc() allocated :)

No, to the at-least-one-byte object that malloc() allocated (but that
you're not allowed to access).
Of course, asking for details about a zero-byte object is pointless,
since an object of zero bytes has no properties other than an
address. It's like asking about properties of a object of type void
(and I don't mean void*).

There are no objects of type void.
Harald's comment about being able to increment a pointer to a
zero-byte object is interesting, though. Should that be legal? IMHO,
the pointer already points to the byte after the end of the object
when you get it, and it'd be UB to try to increment it -- or decrement
it.

C doesn't support zero-byte objects.
 

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,781
Messages
2,569,616
Members
45,306
Latest member
TeddyWeath

Latest Threads

Top