Flash Gordon said:
Keith Thompson wrote, On 28/02/08 07:18:
<snip question of whether pointer returned by malloc must be suitably
alligned for data which would not fit in the space>
In terms of malloc it does since malloc(0) is allowed to return a
non-null pointer and at the very least that is a pointer to a zero
length array of (signed/unsigned/plain) char and it could reasonably
be considered to be a pointer to a 0 length array of any type.
If malloc(0) returns a non-null pointer, then "the behavior is as if
the size were some nonzero value, except that the returned pointer
shall not be used to access an object" (C99 7.20.3p1). It may well be
that that behavior matches the behavior that a pointer to the
(nonexistent) first element of a zero-length array *would* have if the
standard actually talked about zero-length arrays, but it wouldn't be
easy to prove it.
And, of course, an implementation can choose to always return a null
pointer for malloc(0).
The authors of the standard *could* have defined a non-null value
returned by malloc(0) in terms of a zero-length array. Either it
didn't occur to them, or they specifically chose not to, likely
because they didn't want to open that particular can of worms.
I'd be pleased if the standard did treat zero-length arrays, and
perhaps zero-sized structs, consistently. But note that zero-length
arrays, and other zero-sized objects, could introduce all sorts of
confusion. Distinct objects, or array elements, or struct members,
could have the same address; distinct struct members could have the
same offset.
[...]
For the degenerate case of malloc(0) it could be an inconvenience on
occasion. Suppose you have some code of the form...
ptr = malloc(count * sizeof *ptr);
for (i=0; i<count; i++) {
/* do stuff using ptr */
}
free(ptr);
If you cannot safely assign the result returned by malloc because you
could not legally dereference it then you have to special case for
count==0, if you can then it will just work by magic with no need for
a special case.
If you can't assign the result of malloc(), it's not "because you
could not legally dereference it"; after all, it's clear that you can
assign either a null pointer or a pointer just past the end of an
array, even though you can't dereference either of those.
Again, quoting C99 7.20.3p1:
The pointer returned if the allocation succeeds is suitably
aligned so that it may be assigned to a pointer to any type of
object and then used to access such an object or an array of such
objects in the space allocated (until the space is explicitly
deallocated).
I think it's *intended* that (assuming sizeof(int) > 1):
int *ptr;
*ptr = malloc(sizeof(int)/2);
has defined behavior. Assuming malloc() succeeds, the result is a
pointer that's properly aligned for type int, but that points to a
space that's too small to hold an int.
The assignment certainly won't blow up due to misalignment. But the
the standard (a) incorrectly assumes that *dereferencing* a properly
aligned pointer is ok (it's clearly not if the space it points to is
properly aigned but too small) and thereby (b) fails to guarantee that
just *assigning* such a pointer is ok.
To clarify point (a), the standard says the pointer "is suitably
aligned so that it may be ... used to access such an object". This
implies that suitable alignment is enough to guarantee this; it isn't.
It's easy to imagine an implementation in which assigning a pointer
that's properly aligned, but that doesn't point to a sufficiently
large space, can cause a trap. The standard doesn't do enough to
forbid such a trap in this particular case (though it might be a
useful check in some other cases).
Suggested wording:
The pointer returned if the allocation succeeds is suitably
aligned so that it may be assigned to a pointer to any type of
object. If the space allocated is sufficiently large, the pointer
may then be used to access such an object or an array of such
objects in the space allocated (until the space is explicitly
deallocated).
(Perhaps the authors thought that the "sufficiently large" requirement
was so obvious that it didn't need to be stated.)
Note that in the above, including both quotes from the standard and my
own wording, the term "pointer" refers to a pointer *value*, not a
pointer object, and the alignment of a pointer refers to the alignment
of the object it points to, not to the alignment of a pointer object.