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.