[comp.lang.c] Common malloc/free practice violates ANSI standard ?

bdm@anucsd.oz (Brendan McKay) (10/14/89)

[I thought I posted this a month ago, but since I didn't get a single bite,
I suspect it didn't get out.  Apologies if you just thought it was too boring.]

Consider the following piece of code, where OBJ is an arbitrary type name.

        OBJ *objptr;

        objptr = (OBJ*)malloc( sizeof(OBJ) );
        ...
        free( (void*)objptr );

This use of malloc() and free() is of course very commonplace.  [In fact,
the Rationale which accompanies the draft standard has an example which does
it this way.]  However, I wish to argue that the draft ANSI standard does not
permit it.  (Actually, I'm looking at the draft of May 15, 1987, so apologies
if the relevant parts have changed since then.)

malloc() returns a value of type void*, "suitably aligned so that it may be
assigned to a pointer to any type of object and then used to access such an
object..." (Section 4.10.3).  Thus, it is clear that we may use the value
assigned to objptr without worry.  The trouble comes with the call to free(),
who's argument must "match a pointer earlier returned by the [malloc] function".

Consider the following hypothetical implementation:
* pointers are implemented as memory addresses in the common way
* objects of type OBJ require even addresses.
* assigning a void* to an OBJ* involves rounding up to an even address
* malloc allocates sufficient space so that it will cover an object of
  type OBJ even when the void* value returned is rounded up to even.
  (Thus, sometimes it actually allocates one more cell than it is asked for.)
* assigning an OBJ* to a void* involves no change of address

This implementation is unusual, but seems to obey the rules.  There was no
requirement that malloc() return an even address, only that the value
returned could be used after casting to OBJ*.  However, when objptr is cast
back to void* for passing to free(), the value obtained is possibly different
from the one returned by malloc(), thus breaking the rules for free().

No doubt this problem was unintentional.  It could easily be fixed by a
sentence reading something like
"The pointer returned if the allocation succeeds is such that, if it is cast
to a pointer to any type of object and then that pointer is cast to type void*,
the original value is recovered."

Note that I'm not claiming the Standard is broken, only that the writers of
the standard have accidentally ruled out a common coding practice.


Brendan D. McKay
Computer Science Department
Australian National University

bdm@anucsd.oz  or  bdm@anucsd.oz.au

mike@thor.acc.stolaf.edu (Mike Haertel) (10/16/89)

In article <1989Oct14.043811.669@anucsd.oz> bdm@anucsd.oz (Brendan McKay) writes:
>* assigning a void* to an OBJ* involves rounding up to an even address

The standard doesn't say anything about "rounding" pointers.
Assigning a misaligned void* to an OBJ* might well cause a trap when
the OBJ* is dereferenced.  Or even when the void* is just assigned to it!

>* malloc allocates sufficient space so that it will cover an object of
>  type OBJ even when the void* value returned is rounded up to even.
>  (Thus, sometimes it actually allocates one more cell than it is asked for.)

No!  The pointer returned by malloc() always has the most pessimistic
necessary alignment.
-- 
Mike Haertel <mike@stolaf.edu>
``There's nothing remarkable about it.  All one has to do is hit the right
  keys at the right time and the instrument plays itself.'' -- J. S. Bach

cpcahil@virtech.UUCP (Conor P. Cahill) (10/16/89)

In article <1989Oct14.043811.669@anucsd.oz>, bdm@anucsd.oz (Brendan McKay) writes:
> No doubt this problem was unintentional.  It could easily be fixed by a
> sentence reading something like
> "The pointer returned if the allocation succeeds is such that, if it is cast
> to a pointer to any type of object and then that pointer is cast to type
> void*, the original value is recovered."

That is the meaning of the "suitably aligned so that it may be
assigned to a pointer to any type of object and then used to access such an
object..." (Section 4.10.3).

An assignment of a void* pointer that is an invalid address for an OBJ*
will usually cause a core drop at the point of dereference and may cause
a core drop at the assignment.  It *should* not modify the value of the 
pointer.


-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

scott@altos86.Altos.COM (Scott A. Rotondo) (10/16/89)

In article <1989Oct14.043811.669@anucsd.oz> bdm@anucsd.oz (Brendan McKay)
asserts that the pANS does not permit this code:

         OBJ *objptr;

         objptr = (OBJ*)malloc( sizeof(OBJ) );
         ...
         free( (void*)objptr );

> malloc() returns a value of type void*, "suitably aligned so that it
> may be assigned to a pointer to any type of object and then used to
> access such an object..." (Section 4.10.3).  Thus, it is clear that we
> may use the value assigned to objptr without worry.  The trouble comes
> with the call to free(), who's argument must "match a pointer earlier
> returned by the [malloc] function".

> Consider the following hypothetical implementation:
> * pointers are implemented as memory addresses in the common way
> * objects of type OBJ require even addresses.
> * assigning a void* to an OBJ* involves rounding up to an even address
> * malloc allocates sufficient space so that it will cover an object of
>   type OBJ even when the void* value returned is rounded up to even.
>   (Thus, sometimes it actually allocates one more cell than it is asked for.)
> * assigning an OBJ* to a void* involves no change of address

Your hypothetical implementation is not Standard-conforming because of
the fourth point above.  Your quote from section 4.10.3 requires that
the pointer returned from malloc() be aligned according to the most
restrictive alignment requirements for this implementation.  That means
that the cast from void * to OBJ * must leave the address unchanged
(the internal pointer representation may change, but that is
invisible).  Similarly, the cast from OBJ * to void * will not change
the address.

> This implementation is unusual, but seems to obey the rules.  There was no
> requirement that malloc() return an even address, only that the value
> returned could be used after casting to OBJ*.  However, when objptr is cast
> back to void* for passing to free(), the value obtained is possibly different
> from the one returned by malloc(), thus breaking the rules for free().

As indicated above, malloc() must return an even address in this
implementation.  Since neither cast will change the address, free()
will work as expected.

> No doubt this problem was unintentional.  It could easily be fixed by a
> sentence reading something like "The pointer returned if the allocation
> succeeds is such that, if it is cast to a pointer to any type of object
> and then that pointer is cast to type void*, the original value is
> recovered."

This is precisely the meaning of the section 4.10.3 alignment rule.
-- 
===============================================================================
Scott A. Rotondo, Altos Computer Systems			 (408) 946-6700

{sun|pyramid|uunet}!altos86!scott				scott@Altos.COM	

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/16/89)

In article <1989Oct14.043811.669@anucsd.oz> bdm@anucsd.oz (Brendan McKay) writes:
>Note that I'm not claiming the Standard is broken, only that the writers of
>the standard have accidentally ruled out a common coding practice.

No, we didn't -- any valid pointer can be converted to a pointer that
has less strict alignment and back, so that the result compares equal
to the original pointer, void* has the same alignment requirement as
char*, and converting a coarsely-aligned malloc() void* to OBJ* does
NOT result in adjustment of the alignment.  Apply ALL the relevant
constraints and you'll find that there is no problem with typical
malloc()/free() usage.

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/17/89)

In article <1989Oct14.043811.669@anucsd.oz>, bdm@anucsd.oz (Brendan McKay) writes:
|  [  elaborate and nifty argument here  ]
|  
|  Note that I'm not claiming the Standard is broken, only that the writers of
|  the standard have accidentally ruled out a common coding practice.

  You're half right. The wording could have been clarified although I'm
not sure your suggestion is the way to do it. You're wrong in that this
practice has been ruled out. In practice most version of malloc (every
version I've seen) return a quantity alligned so that the block starts
on the most restrictive boundary.

  When I started reading your post I thought you were going to make the
point that you can't have C on a machine which has no single most
restrictive boundary, such as ints start odd and double start even.
Fortunately I can't think of a reason to build such a machine, even to
start an argument.
-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

meissner@twohot.rtp.dg.com (Michael Meissner) (10/17/89)

In article <1279@virtech.UUCP> cpcahil@virtech.UUCP (Conor P. Cahill) writes:
|  In article <1989Oct14.043811.669@anucsd.oz>, bdm@anucsd.oz (Brendan McKay) writes:
|  > No doubt this problem was unintentional.  It could easily be fixed by a
|  > sentence reading something like
|  > "The pointer returned if the allocation succeeds is such that, if it is cast
|  > to a pointer to any type of object and then that pointer is cast to type
|  > void*, the original value is recovered."
|  
|  That is the meaning of the "suitably aligned so that it may be
|  assigned to a pointer to any type of object and then used to access such an
|  object..." (Section 4.10.3).
|  
|  An assignment of a void* pointer that is an invalid address for an OBJ*
|  will usually cause a core drop at the point of dereference and may cause
|  a core drop at the assignment.  It *should* not modify the value of the 
|  pointer.

In implementations where there exist different pointer formats, the
conversion from void * to some pointer may in fact modify the value to
the new format.  Both pointers would in fact be pointing to the same
spot in memory, but have different formats.

To give a concrete example, on the Data General MV computers, there
exist three types of pointers:

   1)	Bit pointers which take two 32 bit double-words (words are 16
	bits to preserve upward migration from the 16 bit Eclipse),
	one of which is a word pointer, and the other is a
	non-negative offset from the word pointer to the desired bit.
	The MV C compiler does not use this form, except for the
	builtin bit functions bitset and bitvalue.

   2)	Byte pointers which take one 32 bit double-word.  The segment
	bits occupy the top three bits, and are usually all one's, due
	to normal user code going in segment (ring) 7.  The next 28
	bits are the word address, and the bottom bit tells which byte
	within the 16-bit word is being accessed.

   3)	Word pointers which take one 32 bit double-word.  The top bit
	is an indirection bit, only if the instruction specified
	indirection, otherwise it is unused.  The C compiler assumes
	for pointers this is 0.  The next three bits are the segment
	of the pointer, and the remaining 28 bits are the word
	address.

To get from a char * or void * pointer to a word pointer, the byte
pointer is shifted right one bit position, losing which particular
byte is pointing to.  One of the checking modes of the compiler,
checks to see if indeed the bottom bit is 0 before doing the
conversion.  The malloc library routine guarantees that it returns a
pointer that is suitably aligned (it aligns to a double word
boundary).
--

Michael Meissner, Data General.				If compiles where much
Uucp:		...!mcnc!rti!xyzzy!meissner		faster, when would we
Internet:	meissner@dg-rtp.DG.COM			have time for netnews?

andre@targon.UUCP (andre) (10/18/89)

In article <1989Oct14.043811.669@anucsd.oz> bdm@anucsd.oz (Brendan McKay) writes:

>Consider the following piece of code, where OBJ is an arbitrary type name.

>        OBJ *objptr;

>        objptr = (OBJ*)malloc( sizeof(OBJ) );
>        ...
>        free( (void*)objptr );
>
>This use of malloc() and free() is of course very commonplace.  [In fact,

Correct.

>malloc() returns a value of type void*, "suitably aligned so that it may be
>assigned to a pointer to any type of object and then used to access such an
>object..." (Section 4.10.3).  Thus, it is clear that we may use the value
>assigned to objptr without worry.  The trouble comes with the call to free(),
>who's argument must "match a pointer earlier returned by the [malloc] function".

The fact that you conclude that the pointer can be changed in value
when cast from void * to <type> * is wrong, because free() could never
know what pointer you used and thus howmany bytes back it much go to
find the pionters of the free list that probably precede your buffer.

This means that when malloc makes sure all types of pointers are
be accomodated, it rounds your request up *and* makes sure that
the address you get is already alligned for the object with the biggest
alignment gap. Meaning that on msdos you are likely to get only even
addresses from malloc. On most unixes addresses on a four byte boundary
and on machines where some type need 8 or 16 or 32 byte boundaries
you get 8 or 16 or 32 byte boundary pointers back from malloc().

You will say that on a big-boundary machine it is not efficient to ask for
10 buffers of 2 char because it will take at least say 10 * 16 byte.
But that is the price you pay for one routine that handles all requests.
You can always make your own char_malloc that askes bigger blocks from malloc
and gives out buffers from that block with byte allingment.

-- 
The mail|    AAA         DDDD  It's not the kill, but the thrill of the chase.
demon...|   AA AAvv   vvDD  DD        Ketchup is a vegetable.
hits!.@&|  AAAAAAAvv vvDD  DD                    {nixbur|nixtor}!adalen.via
--more--| AAA   AAAvvvDDDDDD    Andre van Dalen, uunet!hp4nl!targon!andre

bill@twwells.com (T. William Wells) (10/19/89)

: * assigning a void* to an OBJ* involves rounding up to an even address

: * assigning an OBJ* to a void* involves no change of address

The standard requires that casting from (void*) to (OBJ*) and back
give pointers that compare equal and point to the same object.
Free will have no problems with it.

---
Bill                    { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/20/89)

In article <646@targon.UUCP> andre@targon.UUCP (andre) writes:
>The fact that you conclude that the pointer can be changed in value
>when cast from void * to <type> * is wrong, because free() could never
>know what pointer you used and thus howmany bytes back it much go to
>find the pionters of the free list that probably precede your buffer.

This refutation is wrong -- under the (INCORRECT) assumption that
the original malloc()ed pointer has been rounded up by conversion
to an object pointer, free() could still locate the correct chunk
of memory to liberate, under certain reasonable implementation
assumptions.

However, an error in the refutation does not prove the original
claim, which was mistaken all along.

rhg@cpsolv.UUCP (Richard H. Gumpertz) (10/21/89)

In article <1989Oct19.101306.16791@twwells.com> bill@twwells.com (T. William Wells) writes:
>The standard requires that casting from (void*) to (OBJ*) and back
>give pointers that compare equal and point to the same object.
>Free will have no problems with it.

WRONG!

This is getting tiring.  The standard requires that casting OBJ * to void *
and then back to OBJ * preserve the value.  It does NOT (and intentionally
so) require the reverse.

The description of free requires it to accept a value previously returned
by malloc.  It does not require it to accept a value that was returned from
malloc and then cast to OBJ * and then back to void *.  The standard SHOULD,
however, require this so the programmer does not have to keep an extra,
uncast, pointer around.  Similar for realloc.

Fix the sentences defining free and realloc all is well again.  Why can't people
accept that the standards process overlooked a small nit in the language?  It
was an oversight.  We should be grateful that it was pointed out and fix it in
the next revision.  End of discussion.
-- 
==========================================================================
| Richard H. Gumpertz   rhg@cpsolv.UUCP -or- ...!uunet!amgraf!cpsolv!rhg |
| Computer Problem Solving, 8905 Mohawk Lane, Leawood, Kansas 66206-1749 |
==========================================================================

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/23/89)

In article <423@cpsolv.UUCP> rhg@cpsolv.uucp (Richard H. Gumpertz) writes:
-This is getting tiring.

I agree with THAT.

-The standard requires that casting OBJ * to void * and then back to
-OBJ * preserve the value.  It does NOT (and intentionally so) require
-the reverse.

But it does when the storage pointed to by the void* is already OBJ-aligned,
such as for the (non-null) value returned by malloc().

-The description of free requires it to accept a value previously returned
-by malloc.  It does not require it to accept a value that was returned from
-malloc and then cast to OBJ * and then back to void *.

Yes it does, but in more general terms, of which that is merely one
consequence.

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/23/89)

In article <1989Oct19.101306.16791@twwells.com>, bill@twwells.com (T. William Wells) writes:
|  The standard requires that casting from (void*) to (OBJ*) and back
|  give pointers that compare equal and point to the same object.
|  Free will have no problems with it.

  I think you said this backward (or you misread it). You can cast *to*
a void pointer and *back* without loss of information, but not the
other way 'round. On machines which require allignment at all,
assignment to a pointer to an alligned type may result in loss of
information via rounding.

-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

bill@twwells.com (T. William Wells) (10/25/89)

In article <423@cpsolv.UUCP> rhg@cpsolv.uucp (Richard H. Gumpertz) writes:
: In article <1989Oct19.101306.16791@twwells.com> bill@twwells.com (T. William Wells) writes:
: >The standard requires that casting from (void*) to (OBJ*) and back
: >give pointers that compare equal and point to the same object.
: >Free will have no problems with it.
:
: WRONG!

Bull. Maybe a demonstration is in order? And then maybe you'll
stop nitpicking out of context?

4.10.3

"The pointer returned if the allocation succeeds is suitably
aligned so that it may be assigned to a pointer of any type of
object and then used to access such an object in the space
allocated (until the space is explicitly freed or reallocated).
Each such allocation shall yield a pointer to an object disjoint
from any other object. The pointer returned points to the start
(lowest byte address) of the allocated space."

Thus the malloc yields "a pointer to an object", presented as a
void*, which may then be converted to a pointer to said object.

3.3.4:

"A pointer to an object or incomplete type may be converted to a
pointer to a different object type or a different incomplete type.
The resulting pointer might not be valid if it is improperly
aligned for the type pointed to. It is guaranteed, however, that
a pointer to an object of a given alignment may be converted to a
pointer to an object of the same alignment or a less restrictive
alignment and back again; the result shall compare equal to the
original pointer. (An object that has character type has the
least strict alignment.)"

Thus the object pointer may be converted back to a void*, since
the void* is guaranteed to not have a more restrictive alignment
(by 3.1.2.5 and 3.3.4).

3.3.9:

"If two pointers to object or incomplete types compare equal,
they point to the same object. If two pointers to functions
compare equal, they point to the same function. If two pointers
point to the same object or function, they compare equal."

Since casting does not change the object pointed to, the new
void* must compare equal to the original void*.

QED.

: This is getting tiring.  The standard requires that casting OBJ * to void *
: and then back to OBJ * preserve the value.  It does NOT (and intentionally
: so) require the reverse.

You have been tiring. You keep making your assertion, without
evidence. Now you have the evidence that you are full of shit.

Go away, so that the bandwidth may be used by something more
interesing. Goto, anyone? :-(

---
Bill                    { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com

kremer@cs.odu.edu (Lloyd Kremer) (10/27/89)

I will try to shed some additional light on this, although I may be a fool
to attempt it.  :-)

There is a fundamental difference between
	void *
and
	void *  returned by malloc (or any allocation routine).

In one sense they are opposites.  A void* in general has the most lenient
alignment requirements of any data pointer.  A void* returned by malloc has
the most stringent.

	OBJ* -> void* -> OBJ*
is true for any void*.

	void* -> OBJ* -> void*
is not true for general void*, but is guaranteed true for void* returned
by malloc.

void* is required to be physically identical to char*.  In some
implementations (word oriented architectures), a char* is physically bigger
(has more bits) than pointers to some other objects.  A char* contains a
word address and then a byte offset within the word.  Hence a cast from
a general void* to a smaller pointer type can irreversibly lose data.
A void* returned from malloc is guaranteed to have these additional bits
zero, so the truncation is harmless.

					Lloyd Kremer
					kremer@cs.odu.edu
					Have terminal...will hack!