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!