OK, so what happens if you add or multiply two
short int together and the result is more than 16 bits long? You wind up with an overrange condition. Note that the compiler cannot warn you of this because whether or not this happens will depend entirely on values entered by the user and subsequently computed within the program. Hopefully, you will always consider maximum value cases and choose appropriate data sizes and this won’t be a problem. But what actually happens? To put it simply, the top most bits will be ignored. Consider an 8 bit unsigned integer. It goes from 0 to 255. 255 is represented as eight 1s. What happens if you add the value 1 to this? You get a 9 bit number: a 1 followed by eight 0s. That ninth bit is thrown away as the variable only has eight bits. Thus, 255 plus 1 equals 0! This can create some serious problems! For example, suppose you wanted to use this variable as a loop counter. You want to go through a loop 500 times. The loop will never terminate because an 8 bit integer can’t go up that high. You keep adding one to it, but it keeps flipping back to 0 after it hits 255. This behavior is not all bad; it can, in fact, be put to good use with things like interrupts and timers, as we shall see.
What happens if you mix different types of variables? For example, what happens if you divide a
double by an
int or a
double? C will promote the lesser size/precision types to the larger type and then do the operation. This can sometimes present a problem if you try to assign the result back to something smaller, even if you know it will always “fit”. The compiler will complain if you divide a
long int by another
long int and try to assign the result to a
short int. You can get around this by using a cast. This is your way of telling the compiler that you know there is a potential problem, but to go ahead anyway (hopefully, because you know it will always work, not just because you want to defeat the compiler warning). Casting in C is similar to type conversion in Python (e.g., the
int() function). Here’s an example.
short int x, y=20; long int z=3; x=(short int)(y/z);
Note how you are directing the compiler to turn the division into a
short int. Otherwise, the result is in fact a
long int due to the promotion of
y to the level of
z. What’s the value of
x? Why it’s 6 of course! Remember, the fractional part is meaningless, and thus lost, on integer divides.
Casting is also useful when using math functions. If you have to use
float, you can cast them to/from
double to make use of functions defined with
double. For example, suppose
c are declared as
float but you wish to use the
pow() function to raise
a to the
pow() is defined as taking two
double arguments and returning a double answer.
c = (float)pow( (double)a, (double)b );
This is a very explicit example. Many times you can rely on a “silent cast” promotion to do your work for you as in the integer example above. Sometimes being explicit is a good practice just as a form of documentation.