My co-worker will immediately ask me the question:
memAddress is a 16-bit variable; how can we assign it in an 8-bit variable ‘b’? He does not all those that are going on in the background of C Compiler. We need a simple and rational answer that will satisfy the co-worker.
When you attempt to store a value of one type in an object of another type in C++ or C (by assignment or initialization), two outcomes are possible:
- The code is invalid (“a compile error”).
- The code is perfectly valid. The language will use an implicit conversion, a so called Standard Conversion to transform the value from source type to destination type.
Standard conversions in C++ are performed implicitly. You don’t have to explicitly invoke them through a cast. You can use a cast if you want, but it is not required. There’s a well-defined finite list of allowable Standard Conversions in C++.
So, when such type mismatch occurs, the language looks for a suitable Standard Conversion. If a suitable Standard Conversion exists, it is used. It it doesn’t exist, the code is rejected by the compiler.
int *pi = nullptr;
void *pv1 = pi; // OK
void *pv2 = 5; // Error
The initialization of
pv1 is valid, since C++ language has Standard Conversion from
int * to
void *. The initialization of
pv2 is invalid, since there’s no Standard Conversion from
In out particular case we are we are initializing an
uint8_t variable with a
uint16_t value. Standard Conversions that take place in such contexts do exist. They are called Integral Conversions
The language specification says that the result of such conversion is a unique value of destination type that is congruent to the original value modulo 2N, where N is the bit-width of the destination type. (Congruence is a term related to modulo arithmetics.)
In this case N = 8, 2N = 256, and “congruent” means that we simply divide the original value by 256 and take the remainder. This is why this code
uint16_t memAddress = 0x1234;
uint8_t b = memAddress;
is guaranteed to initialize
0x1234 % 256 = 0x34.
The above quote is taken from C++ standard. The standard of C language has virtually the same specification, albeit worded differently.
That’s why the concept of ‘casting’ is here.
“Casting” does not change anything here. Casting performs the very same conversion as I described above. The only difference is that with a cast the conversion is done explicitly, and without a cast it is done implicitly. The conversion itself is exactly the same in both cases and the result is exactly the same.