Re: C variable retyping

From: Chris Torek (nospam_at_torek.net)
Date: 08/27/04


Date: 27 Aug 2004 19:02:02 GMT

In article <1428bc12.0408270919.22ce3e06@posting.google.com>
Groups User <googlegroups_001@yahoo.com> writes:
>C allows type casting in which a variable is converted from one type
>to another.

This statement is not correct: C's casts convert a *value* from
one type to another, as if by assignment to a temporary variable
whose type is given by the cast.

For instance:

    (double)3

"means" the same thing as:

    double tmp;

    tmp = 3;
    tmp

except that the result of the cast is not an object (the variable
"tmp" above is clearly an object -- if you declare tmp, you can
also have "double *dp = &tmp;" and so on).

>Does C (whatever standard) allow the type of a variable to change,
>within a statement, avoiding the conversion?

No. (Although first we might have to pin down the meaning of
"variable", since the C Standards do not define it, and it turns
out different people have different ideas about this. :-) )

C does, however, allow a subterfuge that, I think, expresses
what you mean to do here:

>Example:
>int a=23;
>int b=34;
>char c='a';
>int * p_int=(int *) &c;
>
>// type casting
>// c is converted into type integer, then b is added.
>(int) c + b;
>
>//retypeing
>(type int) c + b;
>// similar action
>*p_int + b
>
>Ignoring alignment issues, storage size issues, etc., c is retyped to
>an integer, no conversion is done, and c is used, within the
>statement, as an integer, not a char.

I assume you know that, on typical implementations -- at least,
those where *p_int works at all despite those things like alignment
issues -- *p_int accesses some byte(s) that are not at all part of
the object named "c", and hence produces a bizarre value largely
unrelated to the machine's character-code for the letter 'a' (0x61
or 97 if ASCII). On a 16-bit-int machine, for instance, the value
at *p_int might be 0x4061 or 0x6140 if the adjacent byte happens
to be 0x40. Thus, this is not all that useful to begin with.

Nonetheless, we can press on, and use a cast to do the same thing
that *p_int does without using the object named "p_int" here:

    int b = 34;
    char c = 'a';
    ...
    use(*(int *)&c + b);

By taking the address of "c" (as before) -- a value of type "char
*" pointing to the object named "c" -- and converting that pointer
value to "int *" (as before), we get some implementation-defined
and probably useless pointer value of type "int *". The unary "*"
(indirection) operator can be applied immediately to this value,
following this probably-useless pointer and attempting to retrieve
an entire "int", in just the same way that "*p_int" does.

In other words, you may take a pointer value, then use a cast to
"reshape" the value -- perhaps altering it in some major way just
as int-to-double and double-to-int conversions do on today's machines
-- and then, if the new value is actually correct and useful,
indirect through it immediately. If that new value is correct and
useful -- in this case, almost certainly not -- the result is an
object whose type is determined by the cast you used a moment
earlier (minus the initial "pointer to" of course).

Where this is useful, at least in portable, standard-conforming C,
is where the C Standards guarantee that some "intermediate" pointer
form holds all the necessary information so that the result of the
cast is valid. This holds in C99 for all "struct" pointers, and
in both C89 and C99 for "void *" and "char *" pointers, provided
the "intermediate" pointer is first obtained by converting a pointer
that points to the "final" type. For instance:

    double x;
    char *cp;

    cp = (char *)&x;
    *(double *)cp = 3.1415926535897932384626433832795;

is strictly conforming C code, and is guaranteed to set "x" to this
approximation to pi (well, modulo any fuzziness in the implementation's
"double" anyway -- not many will do 30+ decimal digits :-) ). This
is a bizarre thing to do, but it *is* strictly conforming. In more
complicated code, something like this can actually be useful.

In C99, a "struct T *" can always retain all the important bits for
some other "struct U *":

    struct U some_var;
    struct T *tp;
    ...
    tp = (struct T *)&some_var;
    ... /* code that does not change "tp" */ ...
    (*(struct U *)tp).some_u_field = some_val; /* valid */
    ((struct U *)tp)->other_u_field = other_val; /* also valid */

Nobody seems to know of any C89 systems on which the above fails
either, which is probably why the C99 folks decided to allow it
in C99.

Note that the "cast back" may put some "important bits" back into
the pointer we use. In particular, in the "cp = (char *)&x;" case,
word-oriented machines may do shift-and-mask operations on the
underlying pointer values. The (double *)cp operation will "un-shift"
and "re-mask" the pointer, so that *(double *)cp gets at "x" instead
of some unrelated object.

-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
email: forget about it   http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.


Relevant Pages

  • Re: writing library functions and pointers?
    ... On the other hand, pre-ANSI, the cast was ... character pointers, the pointer to the containing word was shifted ... should be the same conversion generated by the cast, ... conversion were done by a cast before the assignment. ...
    (comp.lang.c)
  • Re: two meanings of a cast
    ... as a "let's pretend" cast. ... casts can convert a pointer of one type into a pointer ... There's another such conversion on line four, ... cast converts the value `' to a different type. ...
    (comp.lang.c)
  • Re: writing library functions and pointers?
    ... On the other hand, pre-ANSI, the cast was ... character pointers, the pointer to the containing word was shifted ... should be the same conversion generated by the cast, ... conversion were done by a cast before the assignment. ...
    (comp.lang.c)
  • Re: integer to pinter conversion
    ... While an integer may be converted to a pointer, ... you don't even need the cast in the code above. ... Since the return type is void *, so if the function funcreturns ... conversion is still being done. ...
    (comp.lang.c)
  • Re: POINTER_MAX and POINTER_MIN??
    ... Given that you know what they are when the pointer value is assigned, ... The conversion isn't "silly," because it is essential ... every integer to &stderr would satisfy the Standard. ... If you can't be sure you have a C99 compiler (the adoption ...
    (comp.lang.c)