commom malloc/free practice breaks standard - author strikes back

Andrew P. Mullhaupt amull at Morgan.COM
Sat Oct 21 11:04:44 AEST 1989


In article <992 at cirrusl.UUCP>, dhesi at sun505.UUCP (Rahul Dhesi) writes:
> 
> I, like many others, rely on the gurus in comp.lang.c for much valuable
> information.  Can somebody quote chapter and verse to show that the
> standard does require the series of casts
> 
>      void * ->  OBJ *  ->  void *
> 
> to yield the original pointer?
> 

The short answer seems to be that the standard does not always require
this; and the long answer follows:

(If you don't want to read the long answer; now's the time ...)


Well, in one commonly available source, "C: A Reference Manual" by S. P. 
Hardison and G. L. Steele, Jr., (1987, 1984) we find:

page 115.: "Draft Proposed ANSI C introduces the type 'void *', or pointer
to void, to represent a 'universal' data pointer."

and then 

page 268.: "All other pointer types (except perhaps function pointers) can
be converted to type void * and back without change;"

Which answers the closely related question; that the cast sequence

	OBJ * -> void * -> OBJ *

must not change the pointer to OBJ. In particular, if the pointer to type
void in question is the result of a cast  OBJ * -> void *  then the 
subsequent cast sequence

	void * -> OBJ * -> void *

cannot change the value of the pointer to type void; via the apparent theorem:

	{OBJ * -> void * -> OBJ *} -> void * == {OBJ *} -> void *

where == indicates that the two cast sequences arrive at the same pointer to
void by replacement of the equivalent cast sequences in braces. (The braces 
are not indicative of C syntax, along with the -> symbol for typecasting.)


Now on page 126.: "In general, if the alignment requirement for a type S is at
least as stringent for a type D, (...) then converting a 'pointer to type S'
to a 'pointer to type D' is safe. 'Safe' here means that the resulting pointer
to type D will work as expected if used to fetch or store an object of type D,
and that a subsequent conversion back to the original pointer type will recover
the original pointer. A corollary to this is that any data pointer can be 
converted to type  char *  and back safely."

It seems that the intent of the proposed standard is that the alignment 
requirement for type void is no more stringent than that of any other type.
(This would seem to make sense, since you shouldn't have to align something
you do not ever actually intend to store - an object of type void.)

Further on page 126.: "If the alignment requirement for a type S is less
stringent than that for type D, then the conversion from a 'pointer to type S'
to a 'pointer to type D' could result in either of two kinds of unexpected
behavior. First, an attempt to use the resulting pointer to fetch or store
an object of type D may cause an error, halting the program. Second, the
hardware or implementation may 'adjust' the destination pointer to be legal,
usually by forcing it back to the nearest previous legal address. A subsequent
conversion back to the original pointer type may not recover the original
pointer."

It is entirely clear if you accept that void is no more stringent than any
other type, then types not of this minimal stringency are not guaranteed to
survive the cast sequence in question unless a precondition essentially that
the pointer to void is a legitimate pointer to OBJ before the casting begins.

One should expect that if such a precondition is false, that the desired cast
sequence is not only unguaranteed by the Proposed Draft ANSI Standard, but
likely to be unpleasant at run-time; i.e. a bug.

One often has occasion to cast a pointer to void which is the result of one 
of the alloc family of functions. These return pointers which are guaranteed
to satisfy even the most stringent alignment requirements, (Harbison & Steele
page 345.) One can think of this as being the same as if the pointer to the
desired type was assigned by the allocation function, and then cast to  void *
where by the above theorem it can be safely cast to and from  void *.

I can't find the answer to an even more obscure question arising from this
line of inquiry: It seems that the sometime guarantee of the theorem above
is false in the case that the type  OBJ *  is defined in a typedef with the
volatile type specifier. (I.e. I can't seem to eliminate the scary possibility
of a volatile pointer!) The "obvious" trick is illegal; consider:

typedef OBJ *ptr;		/* ptr is type 'pointer to OBJ' */
typedef volatile ptr BLUNGE;	/* BLUNGE is the volatile kind */

This is explicitly forbidden by Draft Proposed ANSI C, "Typedef names should
not be mixed with other type specifiers" (Harbison & Steele page 116.) I 
haven't had enough experience chasing the lexical rules and syntax to ensure
that no other way to declare a volatile pointer type exists; but I hope it's
impossible. Ominously, we have been able to get  gcc  to accept the declaration

char * volatile p;

but I don't know what it really is or means.

Later,
Andrew Mullhaupt



More information about the Comp.std.c mailing list