[comp.lang.c] increment casted void pointer -- ANSI?

berg@marvin.e17.physik.tu-muenchen.de (Stephen R. van den Berg) (03/08/91)

Suppose I have the following (silly example, I know :-) program:

#include <stdio.h>

main(){static char s[]="abcd";void*p;int a,b;
 p=s;
 a=*(char*)p;
 b=*++(char*)p;
 *--(char*)p=b;
 *++(char*)p=a;
 puts(s);
 return 0;}

Now, you tell me if this is ANSI or K&R or neither, and if it's not ANSI,
how do I do it in ANSI then?

I always thought this was ANSI code, but there seems to be some
compiler on an IBM RT that says otherwise.

Thanks.
--
Sincerely,                 berg@marvin.e17.physik.tu-muenchen.de
           Stephen R. van den Berg.
"I code it in 5 min, optimize it in 90 min, because it's so well optimized:
it runs in only 5 min.  Actually, most of the time I optimize programs."

henry@zoo.toronto.edu (Henry Spencer) (03/09/91)

In article <4142@rwthinf.UUCP> berg@marvin.e17.physik.tu-muenchen.de (Stephen R. van den Berg) writes:
> b=*++(char*)p;
> *--(char*)p=b;
> *++(char*)p=a;
>
>Now, you tell me if this is ANSI or K&R or neither...

Neither.  The result of a cast is not an lvalue, and ++ and -- apply only
to lvalues in either K&R C or ANSI C.

There is no way to say "pretend this variable has a different type" in C.
Casts are conversions, not "view this bit pattern differently" operators
(although historically there was some confusion about this, especially
since some conversions sometimes don't actually require changing the bits).

>how do I do it in ANSI then?

Declare a local variable of type `char *', assign the value of `p' to it,
and then do what you want with the variable.

>I always thought this was ANSI code...

What on Earth made you believe that?
-- 
"But this *is* the simplified version   | Henry Spencer @ U of Toronto Zoology
for the general public."     -S. Harris |  henry@zoo.toronto.edu  utzoo!henry

jfw@ksr.com (John F. Woods) (03/09/91)

berg@marvin.e17.physik.tu-muenchen.de (Stephen R. van den Berg) writes:
>Suppose I have the following (silly example, I know :-) program:
>main(){static char s[]="abcd";void*p;int a,b;
> p=s;
> a=*(char*)p;
> b=*++(char*)p;
> *--(char*)p=b;
> *++(char*)p=a;
> puts(s);
> return 0;}
>Now, you tell me if this is ANSI or K&R or neither,

Neither.  The operand of ++ and -- must be an lvalue; the result of a
cast is an rvalue.

>how do I do it in ANSI then?

Leaving in the "silliness" to make the conceptual changes more clear:

main(){
	static char s[]="abcd";
	void*p;
	char *cp;	/* added */
	char a,b;	/* changed */
	p=s;
	cp = p;		/* added; note that void pointers are not objects
			 * you DO things with; they are typeless bags
			 * for data-pointers which should be assigned to
			 * a correctly-typed pointer for real work.
			 */
	a = *cp;	/*changed*/
	b = *++cp;	/*changed*/
	*--cp = b;	/*changed*/
	*++cp = a;  	/*changed*/
	puts(s);
	return 0;
}

By the way, 
	b = cp[0];
	cp[0] = cp[1];
	cp[1] = b;

is not only clearer, but stands a good chance of running as fast or faster.
Try compiling to assembly code and counting cycles.
Array notation is NOT evil or inefficient.

sarima@tdatirv.UUCP (Stanley Friesen) (03/12/91)

In article <1991Mar8.165300.11692@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
>In article <4142@rwthinf.UUCP> berg@marvin.e17.physik.tu-muenchen.de (Stephen R. van den Berg) writes:
>> b=*++(char*)p;
>> *--(char*)p=b;
>> *++(char*)p=a;
>There is no way to say "pretend this variable has a different type" in C.

Not true, there *is* a way, Stephen just didn't use it.

To pretend a variable has a different type utter the following magic
incantations:

b = *++ *(char **)&p;
*-- *(char **)&p = b;
*++ *(char **)&p = a;


Now, I consider thsi hideous code, but it *does* allow nearly arbitrary
type punning.  [the *effect* is still non-portable]
-- 
---------------
uunet!tdatirv!sarima				(Stanley Friesen)

henry@zoo.toronto.edu (Henry Spencer) (03/13/91)

In article <166@tdatirv.UUCP> sarima@tdatirv.UUCP (Stanley Friesen) writes:
>>There is no way to say "pretend this variable has a different type" in C.
>
>To pretend a variable has a different type utter the following magic
>incantations:
>
>b = *++ *(char **)&p;

Not quite.  This says "pretend that a sequence of bits, starting at the start
of `p', has a different type".  Whether this does the job depends on whether
you take "pretend" to mean "convert it and then convert it back" or "just
view the bits differently".  Consistency with casts would imply the former,
especially since all bets are off if you do the latter.  The original
inquirer clearly wanted the former.
-- 
"But this *is* the simplified version   | Henry Spencer @ U of Toronto Zoology
for the general public."     -S. Harris |  henry@zoo.toronto.edu  utzoo!henry

berg@marvin.e17.physik.tu-muenchen.de (Stephen R. van den Berg) (03/13/91)

Stanley Friesen writes:
>To pretend a variable has a different type utter the following magic
>incantations:

>b = *++ *(char **)&p;

Nice.  Could be usefull to know this (some time).  But, in my original
application (not the example I showed, it was a library replacement of
memmove) I use register void pointers, to which the above trick cannot
be applied (sigh).
--
Sincerely,                 berg@marvin.e17.physik.tu-muenchen.de
           Stephen R. van den Berg.
"I code it in 5 min, optimize it in 90 min, because it's so well optimized:
it runs in only 5 min.  Actually, most of the time I optimize programs."

bright@nazgul.UUCP (Walter Bright) (03/23/91)

In article <1991Mar8.165300.11692@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
/In article <4142@rwthinf.UUCP> berg@marvin.e17.physik.tu-muenchen.de (Stephen R. van den Berg) writes:
/> *++(char*)p=a;
/>Now, you tell me if this is ANSI or K&R or neither...
/Neither.  The result of a cast is not an lvalue, and ++ and -- apply only
/to lvalues in either K&R C or ANSI C.
/There is no way to say "pretend this variable has a different type" in C.
/Casts are conversions, not "view this bit pattern differently" operators

The usual to do a 'type paint', which is to change the type of an object
without changing the bit pattern, is to:
	*(newtype *)&(object)
It's not perfect, i.e. strict ANSI compilers won't let you do this on an lvalue
and it doesn't work on constants, but it usually gets the job done (a good
compiler will optimize away the redundant *&).

gwyn@smoke.brl.mil (Doug Gwyn) (03/27/91)

In article <288@nazgul.UUCP> bright@nazgul.UUCP (Walter Bright) writes:
-In article <1991Mar8.165300.11692@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
-/There is no way to say "pretend this variable has a different type" in C.
-/Casts are conversions, not "view this bit pattern differently" operators
-The usual to do a 'type paint', which is to change the type of an object
-without changing the bit pattern, is to:
-	*(newtype *)&(object)

A strictly conforming method is to use a union type.

bhoughto@pima.intel.com (Blair P. Houghton) (03/27/91)

In article <15589@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>In article <288@nazgul.UUCP> bright@nazgul.UUCP (Walter Bright) writes:
>-In article <1991Mar8.165300.11692@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
>-/There is no way to say "pretend this variable has a different type" in C.
>-The usual to do a 'type paint', which is to change the type of an object
>
>A strictly conforming method is to use a union type.

Ouch!  My ears just pricked up so hard I think I pulled my skull...

Where in the std is it prohibited to fake unions by using a struct
and externally prohibiting attempts to dereference a value
that was superseded by one of a different type?

It seems to say only that a union has to be at least large enough
to hold it's member with the most bits, and to align members
suitably, but not that it has to overlap types.

The rationale parrots the "a pointer to a union, suitably
cast, points to each member".  Considering the latitude
given a cast, this may mean that the pointer could be
adjusted in byte-increments to point to the corresponding
type's member.  This implies strongly that overlap
must occur if there is more than one member with the
same type, but that doesn't buy too much "bit-paint".

I only saw the one paragraph, though (ANSI X3.159-1989,
sec. 3.5.2.1, p. 62, ll. 10-14), so I'd be glad if someone
came up with more stringent, explicit delineation of this
situation.

				--Blair
				  "Time to make the donut holes..."

mouse@thunder.mcrcim.mcgill.edu (der Mouse) (03/27/91)

In article <288@nazgul.UUCP>, bright@nazgul.UUCP (Walter Bright) writes:
> The usual to do a 'type paint', which is to change the type of an
> object without changing the bit pattern, is to:
> 	*(newtype *)&(object)
> It's not perfect, i.e. strict ANSI compilers won't let you do this on
> an lvalue

*except* on an lvalue, I hope you meant.  (Actually, it's something
more like "addressable lvalues" - I don't know the precise wording.)
And you may get garbage when you indirect the resulting pointer.

> and it doesn't work on constants,

They aren't lvalues; you can't apply & to anything but an lvalue.

> but it usually gets the job done

Many not-fully-portable tricks are nevertheless perfectly useful.  Such
a construction is to be discouraged, but not necessarily interdicted
entirely....

					der Mouse

			old: mcgill-vision!mouse
			new: mouse@larry.mcrcim.mcgill.edu

rjohnson@shell.com (Roy Johnson) (03/29/91)

In article <15589@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
> In article <288@nazgul.UUCP> bright@nazgul.UUCP (Walter Bright) writes:
> -In article <1991Mar8.165300.11692@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
> -The usual to do a 'type paint', which is to change the type of an object
> -without changing the bit pattern, is to:
> -	*(newtype *)&(object)

> A strictly conforming method is to use a union type.

So can you do

otype object;
newtype feech;

feech = ((union { newtype n; otype o;})object).n;

or

union {
  newtype n;
  otype   o;
} typeunion;

feech = ((union typeunion)object).n;

or do you have to do

union typeunion blah;

blah.o = object;
feech = blah.n;

?

Just what can we get away with, here?
--
======= !{sun,psuvax1,bcm,rice,decwrl,cs.utexas.edu}!shell!rjohnson =======
Feel free to correct me, but don't preface your correction with "BZZT!"
Roy Johnson, Shell Development Company

gwyn@smoke.brl.mil (Doug Gwyn) (03/29/91)

In article <3492@inews.intel.com> bhoughto@pima.intel.com (Blair P. Houghton) writes:
-In article <15589@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
->In article <288@nazgul.UUCP> bright@nazgul.UUCP (Walter Bright) writes:
->-In article <1991Mar8.165300.11692@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
->-/There is no way to say "pretend this variable has a different type" in C.
->-The usual to do a 'type paint', which is to change the type of an object
->A strictly conforming method is to use a union type.
-Where in the std is it prohibited to fake unions by using a struct
-and externally prohibiting attempts to dereference a value
-that was superseded by one of a different type?

There are constraints on what is strictly conforming use of pointed-to
objects, etc.

-It seems to say only that a union has to be at least large enough
-to hold it's member with the most bits, and to align members
-suitably, but not that it has to overlap types.

Sure it "overlaps", or acts as if it did.  A conforming implementation
that, e.g. stores floating-point data in a different address space from
integer data may have to jump through hoops if it wishes to make the
aliasing of the representations actually work.  However, such a program
would have to be successfully translated, it just may not work right (in
which case the fake pointer business would probably malfunction too).

bhoughto@pima.intel.com (Blair P. Houghton) (03/29/91)

In article <15619@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>Sure it "overlaps", or acts as if it did.  A conforming implementation
>that, e.g. stores floating-point data in a different address space from
>integer data may have to jump through hoops if it wishes to make the
>aliasing of the representations actually work.  However, such a program
>would have to be successfully translated, it just may not work right (in
>which case the fake pointer business would probably malfunction too).

But does this guarantee that you can retrieve the bits of a
member of one type simply by accessing using a different-typed
rvalue?

E.g.

	union {
	    int i;
	    float f;
	} x, *y;
	float z;

	x.i = 4;
	y = &x;
	z = y->f;

It seems to me that this is nowhere permitted to return
the bits of `4' in the float-value, nor prohibited from it.
(I doubt very much that it would return `4.0').  Sure it'd
be a cute thing to have around, but can one _rely_ on it
and claim portability re X3.159-1989?

				--Blair
				  "The standard is undefined..."

gwyn@smoke.brl.mil (Doug Gwyn) (03/30/91)

In article <3548@inews.intel.com> bhoughto@pima.intel.com (Blair P. Houghton) writes:
>	x.i = 4;
>	y = &x;
>	z = y->f;
>It seems to me that this is nowhere permitted to return
>the bits of `4' in the float-value, nor prohibited from it.
>(I doubt very much that it would return `4.0').  Sure it'd
>be a cute thing to have around, but can one _rely_ on it
>and claim portability re X3.159-1989?

No, we were talking about the other way around,
and I wasn't claiming portability, just a method that had to be
accepted by a conforming implementation, although due to variation
in the ways that implementations represent values it would be
expecting too much to ask for portability here.

Certainly one should not be surprised to get an "invalid operand"
exception when attempting the above, as the bit pattern may not
be a valid floating-point representation.

nw@uts.amdahl.com (Neal Weidenhofer) (04/02/91)

In article <15619@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes:
> Sure it "overlaps", or acts as if it did.  A conforming implementation
> that, e.g. stores floating-point data in a different address space from
> integer data may have to jump through hoops if it wishes to make the
> aliasing of the representations actually work.  However, such a program
> would have to be successfully translated, it just may not work right (in
> which case the fake pointer business would probably malfunction too).

I can't find any such requirement in X3.159-1989.  It says in
Sec.3.3.2.3. "...if a member of a union object is accessed after a
value has been stored in a different member of the object, the
behavior is implementation-defined."  I know we all discussed support
for type punning using unions, but it doesn't seem to be required.
The footnote clearly indicates that we intended it to work but
footnotes are, alas, not a part of the standard.

So, as far as I can see, no hoop jumping is required if the
implementation writers/distributors document the fact.  It seems to be
another quality-of-implementation issue.

The opinions expressed above are mine (but I'm willing to share.)

			Regards,
And while the future's          Neal Weidenhofer
	there for anyone        nw@amdahl.uts.amdahl.com
To change, still you            Amdahl Corporation
	know it seems           P. O. Box 3470
It would be easier sometimes    1250 E. Arques Ave. (M/S 316)
	to change the past.     Sunnyvale, CA 94088-3470
				(408)737-5007

gwyn@smoke.brl.mil (Doug Gwyn) (04/03/91)

In article <718q01zZ4fNC00@amdahl.uts.amdahl.com> nw@uts.amdahl.com (Neal Weidenhofer) writes:
>behavior is implementation-defined."  I know we all discussed support
>for type punning using unions, but it doesn't seem to be required.
>The footnote clearly indicates that we intended it to work but
>footnotes are, alas, not a part of the standard.
>So, as far as I can see, no hoop jumping is required if the
>implementation writers/distributors document the fact.  It seems to be
>another quality-of-implementation issue.

Yes, I agree with that assessment.  While the behavior is defined,
not undefined, it needn't be defined to "do the right thing".
I would suggest, however, that implementations SHOULD do the obvious
thing for mixed access to unions in all cases where the member types
share the same (physical) address space.  Thus, using a union to
perform the type punning must be accepted by the compiler, and
ought to actually perform the pun correctly in the vast majority of
environments.  (While we were able to imagine an environment where
floating and integral types were maintained in separate address
spaces, I don't know of any actual implementations like that.)

bhoughto@nevin.intel.com (Blair P. Houghton) (04/04/91)

In article <15675@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>environments.  (While we were able to imagine an environment where
>floating and integral types were maintained in separate address
>spaces, I don't know of any actual implementations like that.)

Is it okay to propose one?

Someone over in comp.<mumble> (I think comp.unix.internals>
mentioned that the i860(tm) has one set of registers for
integers and one for floating-point.  (In fact, any
architecture involving separate CPU and FPU would be that
way, but the i860(tm) does them both on the same chip,
bringing up issues of further transparency by eliminating
the need for external memory or bus operations to implement
a FPU-to-CPU (floating to int) transfer.)

If for some reason the compiler decided that the union should
be handled entirely in register space, the ints and floats
would indeed have different locations.

				--Blair
				  "Yeah, I could beat up Mike Tyson."