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."