Previous | Table of Contents | Next |
Aside from the question of whether division truncates up or down, integer arithmetic is always either exactly defined, if the result of the operation will fit in an appropriately sized integer, or completely undefined, i‹f the result will not fit. In particular, implementations are permitted, but not required, to check for integer overflow, so that implementers can choose the st effective strategy for their particular machines.
In addition to the five arithmetic operators, there are the six relational operators <, >, <=, >=, ==, and !=, and the shift operators << and >>. The right operand of a shift, which says by how many bits to shift the left operand, is required to be non-negative, and it is up to the implementation whether shifting a negative number to the right propagates the sign bit or introduces zeroes.
There are also four bitwise logical operations on integers: and (&), or (|), exclusive or (^), and not (~).
Finally, for all the arithmetic operators, there are corresponding compound assignment operators. For example, if a and b are integral variables, a <<= b is equivalent to a = a << b except that in the former case, a is evaluated only once.
7.2.2.2. Using Integer Arithmetic
Here is an example that uses several of the arithmetic operators, and also demonstrates a few other aspects of C++. It is a function that counts the number of 1-bits in the representation of a long unsigned value:
unsigned short count_bits(long unsigned n) { int result = 0; while (n != 0){ if ((n & 1) != 0) result += 1; n >>= 1; } return result; }
This function has one parameter, which, when the function is called, will be initialized to a copy of the corresponding argument. There is therefore no harm in modifying the parameter, as we do later in the function.
Curly braces ({ }) are used for grouping, similarly to begin and end in Algol or Pascal. The if statement has no corresponding then; in C++, then is an ordinary identifier with no special properties.
Use of whitespace, including indentation, is generally neutral in C++, but can make programs easieror harderto read. Two important exceptions to that neutrality principle are that a newline cannot occur within a string literal, and that preprocessor directives (which begin with #) end at the end of the current line.
The statement
result += 1;
could have been written
++result;
with exactly the same effect. More generally, if e is an expression of built-in type, then (++e) is exactly equivalent to ((e)+=1), and similarly for (--e) and ((e)-=1).
Perhaps a more interesting phenomenonand certainly one that is deeply ingrained in C++ programmingis the postfix ++ operator. Essentially, (e++) is equivalent to ((++e)-1), but this equivalence is not a terribly useful way to understand the operator. It is more useful to say that the value of (e++) is the same as the value of e, but it has the side effect of adding 1 to e after fetching the value of e.
Probably the most common use of postfix ++ is in manipulating array indices. In many languages it is common to say things like
n = a[i]; i = i + 1;
with the notion that after we have fetched a[i], we are done with that value of i so we should increment it to the next value. In C++, the usual way to express such things is
n = a[i++];
After this explanation, the count_bits function should be easy enough to understand. We have counted all the bits in n if and only if all the (remaining) bits are zero, so we will continue counting until that is the case. Each iteration of the loop examines the low-order bit of n, by evaluating n & 1, and increments result if that bit is on. After looking at the low-order bit, the program discards the bit by shifting n right one bit.
7.2.2.3. Truth Values
Our count_bits example has a loop that repeatedly evaluates the condition n != 0. The type of that condition is bool; the possible values of type bool are true and false. It is possible to use a number as a truth value; in such contexts, zero is considered false and any other value is considered true. For that reason, we could have replaced
while (n != 0) { /* ... */ }
with
while (n) { /* ... */ }
However, the latter construction is marginally less clear, because it requires more special knowledge to understand, and what was written was less obviously what was intended.
When a bool value is used as a number, false is treated as 0 and true is treated as 1. So, for example, it is possible to test whether x is between y and z, inclusive, by writing ((x>=y)>(z<=x)).2
2Why one would want to do this in practice, instead of saying ((x>=y)&&(x<=z)), is beyond my understanding; but it makes a nice example.
Three operators take bool arguments and yield bool results: logical and (&&), logical or (||), and logical not (!). The first two of these operators evaluate their right operands only if doing so is necessary to determine the result. So, for example, in an expression like ((x != 0) && ((y/x) > z)), it is guaranteed that y/x will be evaluated only if x is nonzero.
Previous | Table of Contents | Next |