Reputation: 452
I would like to know if there is a difference between:
I would also like to know if there is a good reason to ever use (2) over (1). I have seen (2) in legacy code which is why I was wondering. From the context, I couldn't understand why (2) was being favored over (1). And from the following test I wrote, I have concluded that at least the behavior of an upcast is the same in either case:
/* compile with gcc -lm */
#include <stdio.h>
#include <math.h>
int main(void)
{
unsigned max_unsigned = pow(2, 8 * sizeof(unsigned)) - 1;
printf("VALUES:\n");
printf("%u\n", max_unsigned + 1);
printf("%lu\n", (unsigned long)max_unsigned + 1); /* case 1 */
printf("%lu\n", *((unsigned long *)&max_unsigned) + 1); /* case 2 */
printf("SIZES:\n");
printf("%d\n", sizeof(max_unsigned));
printf("%d\n", sizeof((unsigned long)max_unsigned)); /* case 1 */
printf("%d\n", sizeof(*((unsigned long *)&max_unsigned))); /* case 2 */
return 0;
}
Output:
VALUES:
0
4294967296
4294967296
SIZES:
4
8
8
From my perspective, there should be no differences between (1) and (2), but I wanted to consult the SO experts for a sanity check.
Upvotes: 15
Views: 786
Reputation: 263247
This:
(type)variable
takes the value of variable
and converts it to type type
. This conversion does not necessarily just copy the bits of the representation; it follows the language rules for conversions. Depending on the source and target types, the result may have the same mathematical value as variable
, but it may be represented completely differently.
This:
*((type *)&variable)
does something called aliasing, sometimes informally called type-punning. It takes the chunk of memory occupied by variable
and treats it as if it were an object of type type
. It can yield odd results, or even crash your program, if the source and target types have different representations (say, an integer and a floating-point type), or even if they're of different sizes. For example, if variable
is a 16-bit integer (say, it's of type short
), and type
is a 32-bit integer type, then at best you'll get a 32-bit result containing 16 bits of garbage -- whereas a simple value conversion would have given you a mathematically correct result.
The pointer cast form can also give you alignment problems. If variable
is byte-aligned and type
requires 2-byte or 4-byte alignment, for example, you can get undefined behavior, which could result either in a garbage result or a program crash. Or, worse yet, it might appear to work (which means you have a hidden bug that may show up later and be very difficult to track down).
You can examine the representation of an object by taking its address and converting it to unsigned char*
; the language specifically permits treating any object as an array of character type.
But if a simple value conversion does the job, then that's what you should use.
If variable
and type
are both arithmetic, the cast is probably unnecessary; you can assign an expression of any arithmetic type to an object of any arithmetic type, and the conversion will be done implicitly.
Here's an example where the two forms have very different behavior:
#include <stdio.h>
int main(void) {
float x = 123.456;
printf("d = %g, sizeof (float) = %zu, sizeof (unsigned int) = %zu\n",
x, sizeof (float), sizeof (unsigned int));
printf("Value conversion: %u\n", (unsigned int)x);
printf("Aliasing : %u\n", *(unsigned int*)&x);
}
The output on my system (it may be different on yours) is:
d = 123.456, sizeof (float) = 4, sizeof (unsigned int) = 4
Value conversion: 123
Aliasing : 1123477881
Upvotes: 4
Reputation: 726559
The first cast is legal; the second cast may not be legal.
The first cast tells the compiler to use the knowledge of the type of the variable to make a conversion to the desired type; the compiler does it, provided that a proper conversion is defined in the language standard.
The second cast tells the compiler to forget its knowledge of the variable's type, and re-interpret its internal representation as that of a different type *. This has limited applicability: as long as the binary representation matches that of the type pointed by the target pointer, this conversion will work. However, this is not equivalent to the first cast, because in this situation value conversion never takes place.
Switching the type of the variable being cast to something with a different representation, say, a float
, illustrates this point well: the first conversion produces a correct result, while the second conversion produces garbage:
float test = 123456.0f;
printf("VALUES:\n");
printf("%f\n", test + 1);
printf("%lu\n", (unsigned long)test + 1);
printf("%lu\n", *((unsigned long *)&test) + 1); // Undefined behavior
This prints
123457.000000
123457
1206984705
struct
/union
with the first member being a valid conversion source/target. Otherwise, this leads to undefined behavior. See C 2011 (N1570), 6.5 7, for complete description. Thanks, Eric Postpischil, for pointing out the situations when the second conversion is defined.
Upvotes: 9
Reputation: 145829
What's the difference between “(type)variable” and “*((type *)&variable)”, if any?
The second expression may lead to alignment and aliasing issues.
The first form is the natural way to convert a value to another type. But assuming there is no violation of alignment or aliasing, in some cases the second expression has an advantage over the first form. *((type *)&variable)
will yield a lvalue whereas (type)variable
will not yield a lvalue (the result of a cast is never a lvalue).
This allows you do things like:
(*((type *)& expr)))++
See for example this option from Apple gcc
manual which performs a similar trick:
-fnon-lvalue-assign (APPLE ONLY): Whenever an lvalue cast or an lvalue conditional expression is encountered, the compiler will issue a deprecation warning and then rewrite the expression as follows:
(type)expr ---becomes---> *(type *)&expr cond ? expr1 : expr2 ---becomes---> *(cond ? &expr1 : &expr2)
Upvotes: 2
Reputation: 213338
Let's look at two simple examples, with int
and float
on modern hardware (no funny business).
float x = 1.0f;
printf("(int) x = %d\n", (int) x);
printf("*(int *) &x = %d\n", *(int *) &x);
Output, maybe... (your results may differ)
(int) x = 1 *(int *) &x = 1065353216
What happens with (int) x
is you convert the value, 1.0f
, to an integer.
What happens with *(int *) &x
is you pretend that the value was already an integer. It was NOT an integer.
The floating point representation of 1.0 happens to be the following (in binary):
00111111 100000000 00000000 0000000
Which is the same representation as the integer 1065353216.
Upvotes: 4
Reputation: 6984
Casting the pointer makes a difference when working on a structure:
struct foo {
int a;
};
void foo()
{
int c;
((struct foo)(c)).a = 23; // bad
(*(struct foo *)(&c)).a = 42; // ok
}
Upvotes: 1
Reputation: 409176
The difference is that in the second case you may have undefined behavior. The reason being that unsinged
is the same as unsigned int
and an unsigned long
may be larger than the the unsigned int
, and when casting to a pointer which you dereference you read also the uninitialized part of the unsigned long
.
The first case simply converts the unsigned int
to an unsigned long
with extends the unsigned int
as needed.
Upvotes: 0
Reputation: 106012
First one ((type)variable
is simple casting a variable to desired type and second one (*(type*)&variable
) is derefencing a pointer after being casted by the desired pointer type.
Upvotes: 0