Yet more on malloc and free and the proposed Standard
Mark Brader
msb at sq.sq.com
Wed Dec 13 10:48:37 AEST 1989
This article is posted to comp.lang.c only because I just posted there
mentioning the same topic; followups are directed to comp.std.c.
I have to give a lot of background first; I indent it all by one space,
in case you're using rn and want to skip it, but the new stuff at the end
will refer back to an example in the background.
The topic at issue is whether the proposed Standard (pANS) REQUIRES
[I apologize for the use of block capitals for emphasis in this article,
but I need *'s for indirection.] that the following commonly used code...
TYPE *p; /* TYPE being any object type */
p = malloc (.....);
... /* code using p */
free (p);
...be legitimate, or whether instead it requires that in strict practice
the result of malloc() be saved without being converted to TYPE *, and the
saved value given to free().
void *vp;
TYPE *p;
vp = malloc (.....);
p = vp;
... /* code using p */
free (vp);
It is agreed that the first version works on all existing systems and that
the pANS writers intended it to be required to work; the question is whether
they inadvertently wrote something not restrictive enough.
The critical sentence of the pANS seems to be the following one. This is on
page 155, lines 13-15 (Dec. 1988 version), near the top of section 4.10.3:
# The pointer returned if the allocation [i.e. malloc] 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 ...
But the description of free() in section 4.10.3.2 requires, at line 37
of the same page, that its argument "match a pointer earlier returned by"
malloc() or one of the related functions.
The potential problem is that the word "assigned" may imply a conversion,
and the semantics of conversion from void * (the type returned by malloc())
to TYPE * are undefined, except when the void * pointer was created by
conversion of a TYPE * pointer in the first place, which this one wasn't.
(See section 3.2.2.3 and 3.3.4: when the pointer WAS so obtained, then
on being converted back it regains its original value.)
Now consider the following implementation: Addressing is by the simple
linear model and ints are 2 bytes and must be even-aligned. Conversion
from type void * to int * involves adding 1 if the void * value was not
even-aligned. And malloc(n) may return an odd address, but if it does so
then it will have allocated n+1 bytes.
This example implementation is clearly not in accordance with existing
implementations, where "ints must be even-aligned" would imply "malloc()
only returns even addresses". But it was argued that it would be in
conformance with the pANS, because of that word "assigned to" in 4.10.3.
For example, if malloc(100) returned 1345, then it would have allocated
101 bytes from 1345 to 1445. If TYPE was int in the first example code
above, then p would point to location 1346, and an array of 50 ints
(totaling the requested 100 bytes) could be accessed in locations 1346
to 1445. But the call free(p) would not work, because the correct argument
to give to free() would be a pointer of type void * and value 1345.
It has been suggested that the word "match" in 4.10.3.2 could be interpreted
in such a way that the 1345 and 1346 would match; I don't accept this, as
I find reasonable no other reading for this word than that the pointers would
compare equal using the == operator, which they would not.
It has also been suggested that the word "aligned" was meant to have a
stronger meaning, similar to that in the existing implementations. For
instance, if it was taken to mean that the pointer must have a value
which COULD HAVE been obtained by conversion from any pointer-to-object
type TYPE *. (For instance, in the example this would require it to be
even-aligned.)
In that case, the requirements in 3.2.2.3 and 3.3.4 are activated; when
the void * is converted to TYPE *, it converts to the TYPE * value that it
could have been converted from, and therefore, converting that TYPE * value
back to void * is bound to produce the same void * value as the conversion
that could have happened before would have produced, i.e., the value
returned by malloc(). Therefore the pointer passed to free() will indeed
match the one returned by malloc().
I agree that this may have been what was INTENDED, but I don't think it
is what the standard SAYS.
However, I have what I think is a new and successful argument that really
does require the first example code to be successful. First note the
description of malloc(), from section 4.10.3.3, page 156, lines 6 and 8:
# void *malloc (size_t size);
#
# The malloc function allocates space for an object whose size is
# specified by size ...
And now note again the words from 4.10.3:
# The pointer returned ... 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 ...
Notice the words "in the space allocated"! While the words quoted in
4.10.3.3 do not forbid malloc() from using storage for its own internal
purposes, they do require that the size of the space allocated be what
the argument of malloc specifies.
The hypothetical implementation discussed above may be interpreted in
three ways, each of which violates some part of this.
(a) The space allocated is 101 bytes, from 1345 to 1445. This violates
the "size specified" wording in 4.10.3.3.
(b) The space allocated is 100 bytes, from 1345 to 1444, and location 1445
is overhead. This violates the "in the space allocated" wording in
4.10.3: when the pointer is converted to int * and used to access a
100-byte, 50-int array, location 1445 is accessed.
(c) The space allocated is 100 bytes, from 1346 to 1445, and location 1345
is overhead. This would violate the "in the space allocated" wording
if the value returned by malloc() was converted to char * rather than
int *, whereupon location 1345 might be accessed; and in any case it
also violates line 7 in section 4.10.3, which requires that the pointer
value returned point to the first location in the allocated space.
Thus the hypothetical implementation is NOT conforming, and so, this is
not a bug in the pANS. Unless of course there's something that I've missed.
--
Mark Brader, Toronto "If you feel [that Doug Gwyn] has a bad attitude,
utzoo!sq!msb, msb at sq.com then use lint (or Chris Torek...)" -- Joe English
This article is in the public domain.
More information about the Comp.lang.c
mailing list