Previous Table of Contents Next


7.2.5. Operator Precedence

With all the operators we have encountered, it is important to know how they group, both to make it easier to read other people’s programs and to reduce the number of parentheses.

Unary operators have the highest precedence, and they group right to left. Usually it is meaningless to talk about associativity of unary operators, but in C++ it is crucially important because there are both prefix and postfix unary operators. So, for example, the expression *p++ means the same as *(p++), because unary operators are right associative. We shall discuss the meaning of these expressions later.

Another kind of unary operator is a cast, which is the name of a type enclosed in parentheses. An expression of the form (T)e is the value of e after conversion to type T. The precise nature of this conversion depends on the type of e and the type T; we will discuss some examples of such conversions later.

We shall also discuss later the meanings of the binary operators . and -> and the subscripting and function call “operators” [] and (), which have the same precedence as the unary operators.

Next in precedence come the pointer-to-member operators .* and ->*, which we shall discuss later.

All the arithmetic operators have lower precedence than the ones we have mentioned so far. The arithmetic operators that group the most tightly are *, /, and %, with + and - at the next level down.

Below the arithmetic operators are the shift operators, so that x<<y+z means x<<(y+z) and not (x<<y)+z.

Next come the relational operators, with the unusual property that == and != have lower precedence than the other four. This implies that x<y==y<z means (x<y)==(y<z), which is true if x<y and y<z are both true, or both false.

Next come the bitwise operators &, |, and ^. Each of these has its own precedence, so that a&b^c&d means (a&b)^(c&d) and a^b|c means (a^b)|c. Lower still are the logical operators && and ||, with && binding more tightly than ||.

Next come the assignment operators =, +=, -=, and so on. They all have the same precedence and, uniquely among the binary operators, group from right to left.

Lower still is the only ternary operator, which we have not yet discussed. Expressions that use it take the form e?e1:e2. The expression e is evaluated and converted to bool; if e is true, the result is e1; otherwise it is e2. As you might expect, only one of e1 or e2 is evaluated—never both.

The operator with the lowest precedence is the comma operator. It evaluates its left operand, then discards it and evaluates its right operand, yielding that operand as its result. For example, if i and j are integer variables of the same type, we might say that i++ is equivalent to (j=i, ++i, j) except for its effect on j.

It is important not to confuse precedence with order of evaluation. Most operators (the exceptions being &&, ||, ?:, and ,) evaluate their operands in unspecified order, so that even though precedence demands a particular grouping, that may not say anything about side effects. For example, consider the following function:

   int f(int n)
   {
       cout << n << endl;
       return n;
   }

This function prints its argument as a side effect, and then returns that argument. Suppose that we use it several times in an expression, such as

   f(3) * f(4) + f(5)

There is no question as to the value of this expression: It has the same value as 3*4+5, or 17. But that says nothing about the order in which 3, 4, and 5 are printed, because + could evaluate its operands in either order, and so could *. Programs that depend on side effects in this way are ill advised.

7.2.6. Pointers, References, and Arrays

In keeping with its desire to allow its programs to get close to the hardware, C++ offers two different abstractions for the notion of a machine address. One of these abstractions, the pointer, treats an address as an object in its own right, and associates with each pointer the type of the object to which it (potentially) points. The other abstraction, the reference, treats an address solely as a way of remembering the location of a particular object and referring to that object again later. References are not objects, and the allowable operations on references are severely restricted. Nevertheless, they are useful in a variety of contexts, and absolutely essential in a few of them.

C++ arrays are so closely connected with pointers that it is difficult to describe them separately. Partly for that reason, arrays turn out to be most useful as an implementation medium for library classes. Once such classes exist (and the standard library does, indeed, support them), using built-in arrays directly becomes almost unnecessary. Nevertheless, it is important to describe arrays, because they are so fundamental,3 and because understanding them is essential to understanding other C++ data structures and the structure of the standard library.


3Trenchard More has reportedly said that we know arrays to be the most fundamental data structure because every programmer has at one time or another asked management for arrays.


Previous Table of Contents Next