Computers tend to be more restrictive than C when it comes to arithmetic. For a computer to perform an arithmetic operation, the operands must usually be of the same size (the same number of bits) and be stored in the same way. A computer may be able to add two 16-bit integers directly, but not a 16-bit integer and a 32-bit integer or a 32-bit integer and a 32-bit floating-point number.
C. on the other hand, allows the basic types to be mixed in expressions. We can combine integers, floating-point numbers, and even characters in a single expression. The C compiler may then have to generate instructions that convert
some operands to different types so that the hardware will be able to evaluate the expression. If we add a 16-bit short and a 32-bit int, for example, the compiler will arrange for the short value to be converted to 32 bits. If we add an int and a float, the compiler will arrange for the int to be converted to float format. This conversion is a little more complicated, since int and float values are stored in different ways.
Because the compiler handles these conversions automatically, without the programmer's involvement, they're known as implicit conversions. C also allows the programmer to perform explicit conversions, using the cast operator. I'll discuss implicit conversions first, postponing explicit conversions until later in the section. Unfortunately, the rules for performing implicit conversions are somewhat complex, primarily because C has so many different arithmetic types. Implicit conversions are performed in the following situations:
When the operands in an arithmetic or logical expression don't have the same type. (C performs what are known as the usual arithmetic conversions.)
When the type of the expression on the right side of an assignment doesn't match the type of the variable on the left side.
When the type of an argument in a function call doesn't match the type of the corresponding parameter.
When the type of the expression in a return statement doesn't match the function's return type.
Although C's implicit conversions are convenient, we sometimes need a greater degree of control over type corwersion. For this reason, C provides casts. A cast expression has the form
( type-name ) expression
type-name specifies the type to which the expression should be converted.
The following example shows how to use a cast expression to compute the fractional part of a float value:
float f, frac_part;
frac_part = f - (int) f;
The cast expression (int) f represents the result of converting the value of f to type int. C's usual arithmetic conversions then require that (int) f be converted back to type float before the subtraction can be performed. The difference between f and (int) f is the fractional part of f, which was dropped during the cast.
Cast expressions enable us to document type conversions that would take place anyway:
i = (int) f; /* f is converted to int */
They also enable us to overrule the compiler and force it to do conversions.
Type Definitions and Portability
Type definitions are an important tool for writing portable programs. One of the problems with moving a program from one computer to another is that types may have different ranges on different machines. If i is an int variable, an assignment like
i = 100000;
is fine on a machine with 32-bit integers, but will fail on a machine with 16-bit integers.
For greater portability, consider using typedef to define new names for integer types.
Suppose that we're writing a program that needs variables capable of storing product quantities in the range 0-50,000. We could use long variables for this purpose (since they're guaranteed to be able to hold numbers up to at least 2,147,483,647), but we'd rather use int variables, since arithmetic on int values may be faster than operations on long values; also, int variables may take up less space.
Instead of using the int type to declare quantity variables, we can define our own "quantity" type:
typedef int Quantity;
and use this type to declare variables:
When we transport the program to a machine with shorter integers, we'll change the definition of Quantity:
typedef long Quantity;
This technique doesn't solve all our problems, unfortunately, since changing the definition of Quantity may affect the way Quantity variables are used. At the very least, calls of printf and scanf that use Quantity variables will need to be changed, with %d conversion specifications replaced by %ld.