Variables Top AssignmentOperators Contents

Operators

Way back when, computers were used primarily as calculators. Some whiz-bang programmer would code up some formula and run the program on a bunch of data. In fact, the very first high-level programming language was called Fortran, for formula translation. Fortran, and most every language that came after it, supplied a full complement of mathmatical operators. C is no exception. For example, suppose you have forgotten your times table and aren't quite sure whether 8 times 7 is 54 or 56. We can write a C program to tell us the answer:

    //test
    printf("%d\n",8 * 7);           //should print: 56

Of course, we can multiply variables as well as numbers:

    //test
    int x = 8;
    int y = 7;
    printf("%d\n",x * y);           //should print: 56

We can also intermix variables and numbers:

    //test
    int x = 8;
    printf("%d\n",x * 7);           //should print: 56

Time for some terminology. The multiplication sign * is known as an operator, as it operates on the numbers or variables on either side of it, producing an equivalent literal value. The items preceding and following the multiplication sign are known as operands. It seems that the actual names of various operands are not being taught anymore, so for nostalgia's sake, here they are. The operand to the left of the multiplication sign is known as the multiplicand. The operand to the right is known as the multiplier. The result is known as the product.

The operands of the other basic operators have special names too. For addition, the left operand is known as the augend and the right operand is known as the addend. The result is known as the sum. For subtraction, the left operand is the minuend, the right the subtrahend, and the result as the difference. For division (and I think this is still taught), the left operand is the dividend, the right operand is the divisor, and the result is the quotient.

In general, we will separate operators from their operands by spaces, tabs, or newlines, collectively known as whitespace.14 It's not necessary to do so, but it makes your code easier to read.

C always takes an expression and computes an equivalent literal expression (e.g., integer or real). Most of the C operators are binary, meaning they operate on exactly two operands. We first look at the numeric operators.

Numeric operators

If it makes sense to add two numbers together, you can probably do it in C using the + operator. For example:

    //test
    printf("%d\n",2 + 3);           //should print: 5
    printf("%f\n",1.9 + 3.1);       //should print: 5.000000

One can see that if one adds two integers, the result is an integer. If one does the same with two reals, the result is a real.

Things get more interesting when you add things having different types. Adding an integer and a real (in any order) always yields a real.

    //test
    printf("%f\n",2 + 3.3);         //should print: 5.300000
    printf("%f\n",3.3 + 2);         //should print: 5.300000

Adding a string to a real number yields an error; the types are not "close" enough, like they are with integers and reals. This statment:

    printf("%f\n",2.2 + "123");

generates the following error when compiled:

    error: invalid operands to binary + (have 'double' and 'char *')

In general, when adding two things, the types must match or nearly match.

Subtraction, multiplication, and division of numbers follow the same rules as addition. Of special note is the division operator with respect to integer operands. Consider evaluating the following expression:

    15 / 2

If one asked the C compiler to compute the result, the value 7, not 7.5, would be produced. This is because, when given an integer dividend and an integer divisor, the quotient will be an integer. Since 7.5 is not an integer, the fractional part is lopped off to make it an integer. If we wish to compute a real result, we need to cast the operands:

    //test
    int x = 15, y = 2;
    int i_quotient;
    double r_quotient;
    //calculate!
    i_quotient = x / y;
    r_quotient = (double) x / (double) y;           //(double) is a cast
    printf("%d and %f\n",i_quotient,r_quotient);    //should print: 7 and 7.500000

For division, dividing an integer dividend by a real divisor or vice versa, a real number results. Therefore, we could have used any of the following ways to compute r_quotient.

    r_quotient = (double) x / (double) y;
    r_quotient = (double) x / y;
    r_quotient = x / (double) y;

The complement to integer division is the modulus operator %. While the result of integer division is the quotient, the result of the modulus operator is the remainder. Thus

    14 % 5

evaluates to 4 since 4 is left over when 5 is divided into 14. To check if this is true, one can ask C to compute this expression:

    (14 / 5) * 5 + (14 % 5) == 14

This complicated expression asks the question "is it true that the quotient times the divisor plus the remainder is equal to the original dividend?". Running the following code will show that, indeed, it is true as a 1 is printed.

    $ quickc
    > printf("%d\n",(14 / 5) * 5 + (14 % 5) == 14);
    +
    compiling the program...
    running the program...
    1
    > 

The first set of parentheses in the expression surrounds the quotient while the second pair surrounds the remainder.

We can also use the modulus operator to check whether a value is even or odd. The expression

    x % 2 == 0

evaluates to true if the value of x is even, false otherwise.

Precedence

Precedence (partially) describes the order in which operators, in an expression involving different operators, are evaluated. In C, the expression

    3 + 4 < 10 - 2

evaluates to true. In particular, 3 + 4 and 10 - 2 are evaluated before the <, yielding 7 < 8, which is indeed true. This implies that + and - have higher precedence than <. If < had higher precedence, then 4 < 10 would be evaluated first, yielding 3 + True - 2, which is nonsensical.

Note that precedence is only a partial ordering. We cannot tell, for example whether 3 + 4 is evaluated before the 10 - 2, or vice versa. Upon close examination, we see that it does not matter which is performed first as long as both are performed before the expression involving < is evaluated.

It is common to assume that the left operand is evaluated before the right operand. For the BOOLEAN connectives And and Or, this is indeed true. But for other operators, such an assumption can lead you into trouble. You will learn why later. For now, remember never, never, never depend on the order in which operands are evaluated!

The C language has a complete precedence chart that illustrates operator precedence for all possible operators. This chart includes 15 levels of precedence. However, since it includes a number of operators that we have not yet discussed, we will simply provide you with a subset of this chart for now.

Higher precedence operations are performed before lower precedence operations. Functions which are called with operator syntax have the same precedence level as the mathematical operators.

Parentheses can also be used to change the precedence of operators; in a complicated expression composed of differing operators, subexpressions with parentheses are done first, then dot operations, then division and mulitplication, then addition and subtraction, and so on.

General comment on precedence: unless you have the C Operator Precedence Chart memorized, it is always a good idea to use lots of parentheses whenever you are using multiple operators in an expression or statement. That way, you know exactly how things will be handled.

Finally, you are encouraged to find a complete C Operator Precedence Chart on the web and examine it. It contains lots of useful information.

Associativity

Associativity describes how multiple expressions connected by operators at the same precedence level are evaluated. All the operators, with the exception of the assignment operator, are left associative. For example, the expression 5 - 4 - 3 - 2 - 1 is equivalent to ((((5 - 4) - 3) - 2) - 1). For a left-associative structure, the equivalent, fully parenthesized, structure has open parentheses piling up on the left. If the minus operator was right associative, the equivalent expression would be (5 - (4 - (3 - (2 - 1)))), with the close parentheses piling up on the right. For a commutative operator, it does not matter whether it is left associative or right associative. Subtraction, however, is not commutative, so associativity does matter. For the given expression, the left associative evaluation is -5. If minus were right associative, the evaluation would be 3.

Like subtraction, division, which is non-commutative, exhibits left associativity. In the expression:

    a / b / c / d

the divisions are done from left to right. The above expression is equivalent to:

    (((a / b) / c) / d)

The assignement operator, covered in the next chapter, exhibits right associativity.

Comparing things

Remember the BOOLEAN interpretation of integers? We can use the BOOLEAN comparison operators to generate such values. For example, we can ask if 3 is less than 4:

    //test
    int b = 3 < 4;
    printf("%d\n",b);           //should print: 1

The output says that, indeed, 3 is less than 4, since 1 is considered true. If the expression had resolved to false, the output would be 0, since 0 represents falsity. Besides < (less than), there are other BOOLEAN comparison operators: <= (less than or equal to), >} (greater than), >= (greater than or equal to), == (equal to), and != (not equal to).

Note that two equal signs are used to see if to things are the same. A single equals sign is reserved for the assignment operator, which you will learn about in the next chapter.

Besides integers, we can compare reals with reals and integers and reals using the comparison operators. We cannot compare strings with strings using the BOOLEAN operators, however. We need a special operator to do that. This operator, strcmp, is covered in the chapter on strings.

Combining comparisons

We can combine comparisons with the BOOLEAN logical connectives && and ||, logical AND and logical OR, respectively.

    //test
    int b = 3 < 4 && 4 < 5; //true AND true
    printf("%d\n",b);           //should print: true (which is 1)
    b = 3 < 4 || 5 < 4; //true OR false
    printf("%d\n",b);           //should print: true (which is 1)
    b = 3 < 4 && 5 < 4; //true AND false
    printf("%d\n",b);           //should print: false (which is 0)

The first bit of code asks if both the expression 3 < 4 and the expression 4 < 5 are true. Since both are, C calculates a 1. The second bit of code asks if at least one of the expressions is true. Again, C calculates a 1. The difference between && and || is illustrated by the last two interactions. Since only one expression is true (the latter expression being false) only the || operator yields a true value.

There is one more BOOLEAN logic operation, called not. It simply reverses the value of the expression to which it is attached. The not operator is represented by an exclamation point (Computer Scientists call it 'bang'):

    int b = !(3 < 4 && 4 < 5);
    printf("%d\n",b);           //should print false (which is 0)
    b = !(3 < 4 || 5 < 4);
    printf("%d\n",b);           //should print false (which is 0)
    b = !(3 < 4 && 5 < 4);
    printf("%d\n",b);           //should print true  (which is 1)

Note that we attached not to each of the previous expressions involving the logical connectives. Note also that the calculated result is reversed from before in each case.

In terms of precedence, && and || are lower than the comparison operators, which in turn are lower than the mathmatical operators.

lusth@cs.ua.edu


Variables Top AssignmentOperators Contents