How to Read a Complex C++ Expression
C++ is full of little symbols, each of which adds to the meaning of expressions. The rules of C++ grammar are so flexible that these symbols can be combined in almost impenetrably complex combinations. Expressions in the simpler C language can get so obtuse that there used to be an annual contest for who could write the most obscure program and who could understand it.
It’s never a good idea to try to write complex code but you will sometimes run across expressions in C++ that are a bit bewildering at first glance. Just use the following steps to figure them out:
Start at the most embedded parentheses.
Start looking for the outer most parentheses. Within those, look for embedded parentheses. Repeat the process until you’ve worked your way to the deepest pair of parentheses. Start evaluating that subexpression first using the following rules. Once you understand that expression, pop back out to the next level and repeat the process.
Within the pair of parentheses, evaluate each operation in order of precedence.
The order that operators are evaluated is determined by the operator’s precedence shown in the table. Indirection comes before multiplication which comes before addition thus the following adds 1 plus 2 times the value pointed at by *ptr.
int i = 1 + 2 * *ptr;
|1||() (unary)||Invoke a function|
|2||* and -> (unary)||Dereference a pointer|
|2||– (unary)||Returns the negative of its argument|
|6||&& (binary)||Logical AND|
|7||=, *=,%=,+=,-= (special)||Assignment types|
Evaluate operations of the same precedence from left to right (except assignment, which goes the other way).
Most operators of the same precedence evaluate from left to right. Thus the following adds 1 to 2 and adds the result to 3:
int i = 1 + 2 + 3;
The order of evaluation of some operators doesn’t matter. For example, addition works the same from left to right as it does from right to left. The order of evaluation makes a lot of difference for some operations like division. The following divides 8 by 4 and divides the result by 2:
int i = 8 / 4 / 2;
The main exception to this rule is assignment, which is evaluated from right to left:
a = b = c;
This assigns c to b and the result to a.
Evaluate subexpressions in no particular order.
Consider the following expression:
int i = f() + g() * h();
Multiplication has higher precedence, so you might assume that the functions g() and h() are called before f(), however, this isn’t the case. Function call has the highest precedence of all, so all three functions are called before either the multiplication or the addition is performed. (The results returned from g() and h() are multiplied and then added to the results returned from f().)
The only time that the order that functions are called makes a difference is when the function has side effects such as opening a file or changing the value of a global variable. You should definitely not write your programs so that they depend upon these type of side effects.
Perform any type conversions only when necessary.
You should not make more type conversions than absolutely necessary. For example, the following expression has at least three and possibly four type conversions:
float f = 'a' + 1;
The char ‘a’ must be promoted to an int to perform the addition. The int is then converted to a double and then down converted to a single precision float. Remember that all arithmetic is performed either in int or double. You should generally avoid performing arithmetic on character types and avoid single precision float altogether.