Why do we cast integer values to pointer types in C

1

I have tried finding an answer to this but I could not. Say I want to write a value at some specific address in memory. Say I want to write 7 at address 0x80005000.

So I do something like this:

uint32_t *ptr = (uint32_t* )0x80005000;
*ptr = 7;

What I don't understand is why do we need to typecast that hex value of the address to a pointer type. What can go wrong if we do it without the cast?

c
embedded
asked on Stack Overflow Apr 23, 2019 by Cantaff0rd • edited Apr 23, 2019 by alinsoar

2 Answers

0

The type of 0x80005000 is (probably) int, which is not a pointer and can't be assigned to a pointer-type variable without casting (well technically it can, but the compiler should complain about it).

Casting the value tells the compiler "the type of this value is really something else than you think it is, so it's okay".

0

Casting is basically a way to tell the compiler that "Yes, I'm sure what I'm doing and I will take responsibility for any consequences."

In your particular case, the main reason for the cast is to hush the compiler, so that it does not emit a warning. The code would compile (unless you compile with the parameter -pedantic-errors) and is not unlikely to work well without the cast.

However, do keep in mind that without the cast you will invoke undefined behavior. The C11 standard 6.5.16.1 says:

One of the following shall hold:112)

  • the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
  • the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • the left operand is an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
  • the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.

The two fattened points are the ones relevant here, and none of them is fulfilled without a cast.

Quote from Lundin in the comments:

For example given unsigned* ptr = NULL; ... ptr = 0x12345678; foo = *ptr; might in theory lead to the compiler accessing address NULL. Because during optimization the compiler is free to assume that ptr was never assigned a value, since no valid form of pointer assignment is present in the code.

But there are other cases where casting is used for some trickery. A classic example is the Carmack trick used in the original Doom engine. Here is the code with original comments:

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
    i  = 0x5f3759df - ( i >> 1 );               // what the fuck? 
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

    return y;
}

NOTE The above code causes undefined behavior, and should NOT be considered an example of good C code.

Also, some people use casting to hush the compiler even when they should not. Remember that hushing the compiler is always a risk. Here is an example of how it can go wrong:

int main()
{
    char c = 'h';
    float a = 0.123;
    printf("%c %f\n", c, a);
    int *cp = (int *) &c;
    *cp = 'h';
    double *ap = (double*) &a;
    *ap = 0.123;
    printf("%c %f\n", c, a);
}

And the output. These two lines "should" be the same.

$ ./a.out 
h 0.123000
? -0.000000

Casting is a very dangerous thing. Always think twice before a cast.

answered on Stack Overflow Apr 23, 2019 by klutt • edited Apr 23, 2019 by klutt

User contributions licensed under CC BY-SA 3.0