eeowaa
eeowaa

Reputation: 452

What's the difference between "(type)variable" and "*((type *)&variable)", if any?

I would like to know if there is a difference between:

  1. Casting a primitive variable to another primitive type
  2. Dereferencing a cast of a primitive variable's address to a pointer of another primitive type

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

Answers (7)

Keith Thompson
Keith Thompson

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

Sergey Kalinichenko
Sergey Kalinichenko

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

(demo)


* This is valid only when one of the types is a character type and the pointer alignment is valid, type conversion is trivial (i.e. when there is no conversion), when you change qualifiers or signedness, or when you cast to/from a 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

ouah
ouah

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

Dietrich Epp
Dietrich Epp

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

ensc
ensc

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

Some programmer dude
Some programmer dude

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

haccks
haccks

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

Related Questions