- Understanding Data Types and Constants
- Working with Variables
- Working with Arithmetic Expressions
- Combining Operations with Assignment: The Assignment Operators
- Types _Complex and _Imaginary
- Exercises
Working with Arithmetic Expressions
In C, just as in virtually all programming languages, the plus sign (+) is used to add two values, the minus sign (−) is used to subtract two values, the asterisk (*) is used to multiply two values, and the slash (/) is used to divide two values. These operators are known as binary arithmetic operators because they operate on two values or terms.
You have seen how a simple operation such as addition can be performed in C. Program 3.2 further illustrates the operations of subtraction, multiplication, and division. The last two operations performed in the program introduce the notion that one operator can have a higher priority, or precedence, over another operator. In fact, each operator in C has a precedence associated with it. This precedence is used to determine how an expression that has more than one operator is evaluated: The operator with the higher precedence is evaluated first. Expressions containing operators of the same precedence are evaluated either from left to right or from right to left, depending on the operator. This is known as the associative property of an operator. Appendix A provides a complete list of operator precedences and their rules of association.
Program 3.2 Using the Arithmetic Operators
// Illustrate the use of various arithmetic operators #include <stdio.h> int main (void) { int a = 100; int b = 2; int c = 25; int d = 4; int result; result = a - b; // subtraction printf ("a - b = %i\n", result); result = b * c; // multiplication printf ("b * c = %i\n", result); result = a / c; // division printf ("a / c = %i\n", result); result = a + b * c; // precedence printf ("a + b * c = %i\n", result); printf ("a * b + c * d = %i\n", a * b + c * d); return 0; }
Program 3.2 Output
a - b = 98 b * c = 50 a / c = 4 a + b * c = 150 a * b + c * d = 300
After declaring the integer variables a, b, c, d, and result, the program assigns the result of subtracting b from a to result and then displays its value with an appropriate printf() call.
The next statement
result = b * c;
has the effect of multiplying the value of b by the value of c and storing the product in result. The result of the multiplication is then displayed using a printf() call that should be familiar to you by now.
The next program statement introduces the division operator—the slash. The result of 4, as obtained by dividing 100 by 25, is displayed by the printf() statement immediately following the division of a by c.
On some computer systems, attempting to divide a number by zero results in abnormal termination of the program.2 Even if the program does not terminate abnormally, the results obtained by such a division will be meaningless.
In Chapter 5, you see how you can check for division by zero before the division operation is performed. If it is determined that the divisor is zero, an appropriate action can be taken and the division operation can be averted.
The expression
a + b * c
does not produce the result of 2550 (102 × 25); rather, the result as displayed by the corresponding printf() statement is shown as 150. This is because C, like most other programming languages, has rules for the order of evaluating multiple operations or terms in an expression. Evaluation of an expression generally proceeds from left to right. However, the operations of multiplication and division are given precedence over the operations of addition and subtraction. Therefore, the expression
a + b * c
is evaluated as
a + (b * c)
by the C programming language. (This is the same way this expression would be evaluated if you were to apply the basic rules of algebra.)
If you want to alter the order of evaluation of terms inside an expression, you can use parentheses. In fact, the expression listed previously is a perfectly valid C expression. Thus, the statement
result = a + (b * c);
could have been substituted in Program 3.2 to achieve identical results. However, if the expression
result = (a + b) * c;
were used instead, the value assigned to result would be 2550 because the value of a (100) would be added to the value of b (2) before multiplication by the value of c (25) would take place. Parentheses can also be nested, in which case evaluation of the expression proceeds outward from the innermost set of parentheses. Just be certain you have as many closed parentheses as you have open ones.
You will notice from the last statement in Program 3.2 that it is perfectly valid to give an expression as an argument to printf() without having to first assign the result of the expression evaluation to a variable. The expression
a * b + c * d
is evaluated according to the rules stated previously as
(a * b) + (c * d)
or
(100 * 2) + (25 * 4)
The result of 300 is handed to the printf() routine.
Integer Arithmetic and the Unary Minus Operator
Program 3.3 reinforces what you just learned and introduces the concept of integer arithmetic.
Program 3.3 More Examples with Arithmetic Operators
// More arithmetic expressions #include <stdio.h> int main (void) { int a = 25; int b = 2; float c = 25.0; float d = 2.0; printf ("6 + a / 5 * b = %i\n", 6 + a / 5 * b); printf ("a / b * b = %i\n", a / b * b); printf ("c / d * d = %f\n", c / d * d); printf ("-a = %i\n", -a); return 0; }
Program 3.3 Output
6 + a / 5 * b = 16 a / b * b = 24 c / d * d = 25.000000 -a = -25
Extra blank spaces are inserted between int and the declaration of a, b, c, and d in the first four statements to align the declaration of each variable. This helps make the program more readable. You also might have noticed in each program presented thus far that a blank space was placed around each operator. This, too, is not required and is done solely for aesthetic reasons. In general, you can add extra blank spaces just about anywhere that a single blank space is allowed. A few extra presses of the spacebar prove worthwhile if the resulting program is easier to read.
The expression in the first printf() call of Program 3.3 reinforces the notion of operator precedence. Evaluation of this expression proceeds as follows:
- Because division has higher precedence than addition, the value of a (25) is divided by 5 first. This gives the intermediate result of 5.
- Because multiplication also has higher precedence than addition, the intermediate result of 5 is next multiplied by 2, the value of b, giving a new intermediate result of 10.
- Finally, the addition of 6 and 10 is performed, giving a final result of 16.
The second printf() statement introduces a new twist. You would expect that dividing a by b and then multiplying by b would return the value of a, which has been set to 25. But this does not seem to be the case, as shown by the output display of 24. It might seem like the computer lost a bit somewhere along the way. The fact of the matter is that this expression was evaluated using integer arithmetic.
If you glance back at the declarations for the variables a and b, you will recall that they were both declared to be of type int. Whenever a term to be evaluated in an expression consists of two integers, the C system performs the operation using integer arithmetic. In such a case, all decimal portions of numbers are lost. Therefore, when the value of a is divided by the value of b, or 25 is divided by 2, you get an intermediate result of 12 and not 12.5 as you might expect. Multiplying this intermediate result by 2 gives the final result of 24, thus explaining the “lost” digit. Don’t forget that if you divide two integers, you always get an integer result. In addition, keep in mind that no rounding occurs, the decimal value is simply dropped, so integer division that ends up with 12.01, 12.5, or 12.99 will end up with the same value—12.
As you can see from the next-to-last printf() statement in Program 3.3, if you perform the same operation using floating-point values instead of integers, you obtain the expected result.
The decision of whether to use a float variable or an int variable should be made based on the variable’s intended use. If you don’t need any decimal places, use an integer variable. The resulting program is more efficient—that is, it executes more quickly on many computers. On the other hand, if you need the decimal place accuracy, the choice is clear. The only question you then must answer is whether to use a float, double, or long double. The answer to this question depends on the desired accuracy of the numbers you are dealing with, as well as their magnitude.
In the last printf() statement, the value of the variable a is negated by use of the unary minus operator. A unary operator is one that operates on a single value, as opposed to a binary operator, which operates on two values. The minus sign actually has a dual role: As a binary operator, it is used for subtracting two values; as a unary operator, it is used to negate a value.
The unary minus operator has higher precedence than all other arithmetic operators, except for the unary plus operator (+), which has the same precedence. So the expression
c = -a * b;
results in the multiplication of −a by b. Once again, in Appendix A you will find a table summarizing the various operators and their precedences.
The Modulus Operator
A surprisingly valuable operator, one you may not have experience with, is the modulus operator, which is symbolized by the percent sign (%). Try to determine how this operator works by analyzing Program 3.4.
Program 3.4 Illustrating the Modulus Operator
// The modulus operator #include <stdio.h> int main (void) { int a = 25, b = 5, c = 10, d = 7; printf("a = %i, b = %i, c = %i, and d = %i\n", a, b, c, d); printf ("a %% b = %i\n", a % b); printf ("a %% c = %i\n", a % c); printf ("a %% d = %i\n", a % d); printf ("a / d * d + a %% d = %i\n", a / d * d + a % d); return 0; }
Program 3.4 Output
a = 25, b = 5, c = 10, and d = 7 a % b = 0 a % c = 5 a % d = 4 a / d * d + a % d = 25
The first statement inside main() defines and initializes the variables a, b, c, and d in a single statement.
For a reminder, before a series of statements that use the modulus operator are printed, the first printf() statement prints the values of the four variables used in the program. It’s not crucial, but it’s a nice reminder to help someone follow along with your program. For the remaining printf() lines, as you know, printf() uses the character that immediately follows the percent sign to determine how to print the next argument. However, if it is another percent sign that follows, the printf() routine takes this as an indication that you really intend to display a percent sign and inserts one at the appropriate place in the program’s output.
You are correct if you concluded that the function of the modulus operator % is to give the remainder of the first value divided by the second value. In the first example, the remainder after 25 is divided by 5 and is displayed as 0. If you divide 25 by 10, you get a remainder of 5, as verified by the second line of output. Dividing 25 by 7 gives a remainder of 4, as shown in the third output line.
The last line of output in Program 3.4 requires a bit of explanation. First, you will notice that the program statement has been written on two lines. This is perfectly valid in C. In fact, a program statement can be continued to the next line at any point at which a blank space could be used. (An exception to this occurs when dealing with character strings—a topic discussed in Chapter 9, “Character Strings.”) At times, it might not only be desirable, but perhaps even necessary, to continue a program statement onto the next line. The continuation of the printf() call in Program 3.4 is indented to visually show that it is a continuation of the preceding program statement.
Turn your attention to the expression evaluated in the final statement. You will recall that any operations between two integer values in C are performed with integer arithmetic. Therefore, any remainder resulting from the division of two integer values is simply discarded. Dividing 25 by 7, as indicated by the expression a / d, gives an intermediate result of 3. Multiplying this value by the value of d, which is 7, produces the intermediate result of 21. Finally, adding the remainder of dividing a by d, as indicated by the expression a % d, leads to the final result of 25. It is no coincidence that this value is the same as the value of the variable a. In general, the expression
a / b * b + a % b
will always equal the value of a, assuming of course that a and b are both integer values. In fact, the modulus operator % is defined to work only with integer values.
As far as precedence is concerned, the modulus operator has equal precedence to the multiplication and division operators. This implies, of course, that an expression such as
table + value % TABLE_SIZE
will be evaluated as
table + (value % TABLE_SIZE)
Integer and Floating-Point Conversions
To effectively develop C programs, you must understand the rules used for the implicit conversion of floating-point and integer values in C. Program 3.5 demonstrates some of the simple conversions between numeric data types. You should note that some compilers might give warning messages to alert you of the fact that conversions are being performed.
Program 3.5 Converting Between Integers and Floats
// Basic conversions in C #include <stdio.h> int main (void) { float f1 = 123.125, f2; int i1, i2 = -150; char c = 'a'; i1 = f1; // floating to integer conversion printf ("%f assigned to an int produces %i\n", f1, i1); f1 = i2; // integer to floating conversion printf ("%i assigned to a float produces %f\n", i2, f1); f1 = i2 / 100; // integer divided by integer printf ("%i divided by 100 produces %f\n", i2, f1); f2 = i2 / 100.0; // integer divided by a float printf ("%i divided by 100.0 produces %f\n", i2, f2); f2 = (float) i2 / 100; // type cast operator printf ("(float) %i divided by 100 produces %f\n", i2, f2); return 0; }
Program 3.5 Output
123.125000 assigned to an int produces 123 -150 assigned to a float produces -150.000000 -150 divided by 100 produces -1.000000 -150 divided by 100.0 produces -1.500000 (float) -150 divided by 100 produces -1.500000
Whenever a floating-point value is assigned to an integer variable in C, the decimal portion of the number gets truncated. So, when the value of f1 is assigned to i1 in the previous program, the number 123.125 is truncated, which means that only its integer portion, or 123, is stored in i1. The first line of the program’s output verifies that this is the case.
Assigning an integer variable to a floating variable does not cause any change in the value of the number; the value is simply converted by the system and stored in the floating variable. The second line of the program’s output verifies that the value of i2 (−150) was correctly converted and stored in the float variable f1.
The next two lines of the program’s output illustrate two points that must be remembered when forming arithmetic expressions. The first has to do with integer arithmetic, which was previously discussed in this chapter. Whenever two operands in an expression are integers (and this applies to short, unsigned, long, and long long integers as well), the operation is carried out under the rules of integer arithmetic. Therefore, any decimal portion resulting from a division operation is discarded, even if the result is assigned to a floating variable (as you did in the program). Therefore, when the integer variable i2 is divided by the integer constant 100, the system performs the division as an integer division. The result of dividing −150 by 100, which is −1, is, therefore, the value that is stored in the float variable f1.
The next division performed in the previous listing involves an integer variable and a floating-point constant. Any operation between two values in C is performed as a floating-point operation if either value is a floating-point variable or constant. Therefore, when the value of i2 is divided by 100.0, the system treats the division as a floating-point division and produces the result of −1.5, which is assigned to the float variable f1.
The Type Cast Operator
The last division operation from Program 3.5 that reads
f2 = (float) i2 / 100; // type cast operator
introduces the type cast operator. The type cast operator has the effect of converting the value of the variable i2 to type float for purposes of evaluation of the expression. In no way does this operator permanently affect the value of the variable i2; it is a unary operator that behaves like other unary operators. Because the expression −a has no permanent effect on the value of a, neither does the expression (float) a.
The type cast operator has a higher precedence than all the arithmetic operators except the unary minus and unary plus. Of course, if necessary, you can always use parentheses in an expression to force the terms to be evaluated in any desired order.
As another example of the use of the type cast operator, the expression
(int) 29.55 + (int) 21.99
is evaluated in C as
29 + 21
because the effect of casting a floating value to an integer is one of truncating the floating-point value. The expression
(float) 6 / (float) 4
produces a result of 1.5, as does the following expression:
(float) 6 / 4