pointer - upcast shared_ptr

Casting a pointer-What is the difference at runtime? (4)

Consider the following small example code:

#include <stdio.h>
#include <stdlib.h>

int main()
    int *i;
    char *c1, *c2;

    i = malloc(4);

    *i = 65535;

    c1 = i;
    c2 = (char *)i;

    printf("%p %p %p\n", i, c1, c2);
    printf("%d %d", *c1, *c2);


    return 0;

In the example, I allocate memory to store an integer, which is pointed by i. Then, I store the value 65535 (1111 1111 1111 1111) in *i. The next thing I do is make two char* pointers also point to the integer. I do it two times, but in two different ways: c1 = i; and c2 = (char *)i;. Finally, I print all pointers and all values in the screen. The three pointers are pointing to the same address, and the two values *c1 and *c2 are correct (-1).

However, the compiler generates a warning in this line: c1 = i;. The warning is generated because I did not use the (char *) cast to do the assignment.

What I would like to ask is why the compiler generates this warning, since I do not see any difference in using c1 = i; or c2 = (char *)i;. In both cases, the result is the same address with the same size in bytes. And this is valid for all casts, even if it is a (int *) cast, (float *) cast, (short *) cast, etc. All of them generate the same value, but the compiler will only accept it without a warning if the cast being used is of the pointer's type.

I really would like to know why the compiler asks for that cast, even if the result is always the same.

Compiler only knows about converting a generic pointer void * to any pointer and vice-versa implicitly. Other than generic pointer, it expects a cast before pointer assignment to make pointers of compatible type and decides that the assignment is done knowingly.

In more complex solutions there may be problems if you don't do that. It doesn't cause problems in this case, but when the compiler needs to treat the pointed memory location by its type, it may treat it wrong. Take note that char is 1 byte long, while int is 4 or 8 bytes long.

Unfortunately the best answer is in the comments to the question!

See @PieterWitvoet commment:

In C, the type system is a compile-time 'tool' that prevents you from performing invalid operations. Things of different types have different operations available to them, and the compiler is making sure you're only using valid operations. But some operations meant for one type may work just fine for another type, so when you want to use those you can perform a type-cast, which basically tells the compiler: "I know what I'm doing, just pretend that this thing here is of the type that's valid for this operation".

In case you still not get the point: C is a statically typed language. What this means is that the programmer should define the type of the identifiers before using them. Thus, one of the purpose of a C compiler is to ensure that you have assigned a type to all the identifiers. Another purpose would be to ensure that values assigned are of the same type as that of the identifier to which they are assigned. In case of pointers, they are internally of same type i.e.., they are 32-bits on 32-bits systems. However, the high level aspect of the pointers are that they are of pointers to a certain 'type' of data. As such it makes sense to ensure that a pointer is be assigned an address of a data that is of the expected type. I vaguely remember that such a check didn't originally exist and was added in some later release of the C standard. However, I could be wrong. If such a check is not in place then one could end up storing the address of wrong data and this would materialize as a run time error. For example assigning the address of a struct to an int/char/any other type and then indirecting in to the same assuming that it is of the concerned struct type could lead to unexpected problems at run time. It is not that such assignments are invalid, as such, the compiler warns you and if you type cast the value then the compiler would not worry about it because it then takes it that you know what you are doing.

When you use:

c2 = i;

the compiler warns you about the assignment of type int* to char*. It could potentially be an inadvertent error. The compiler is warning you with the hope that, if it is indeed an inadvertent error, you have the chance to fix it.

When you use:

c2 = (char *)i;

you are telling the compiler that you, as the programmer, know what you are doing.