I l@ve RuBoard |
![]() ![]() |
1.3 Writing ExpressionsThe built-in data types are supported by a collection of arithmetic, relational, logical, and compound assignment operators. The arithmetic operators are unsurprising except for integer division and the remainder operator: // Arithmetic Operators + addition a + b - subtraction a - b * multiplication a * b / division a / b % remainder a % b The division of two integer values yields a whole number. Any remainder is truncated; there is no rounding. The remainder is accessed using the % operator: 5 / 3 evaluates to 1 while 5 % 3 evaluates to 2 5 / 4 evaluates to 1 while 5 % 4 evaluates to 1 5 / 5 evaluates to 1 while 5 % 5 evaluates to 0 When might we actually use the remainder operator? Imagine that we want to print no more than eight strings on a line. If the number of words on the line is less than eight, we output a blank space following the word. If the string is the eighth word on the line, we output a newline. Here is our implementation: const int line_size = 8; int cnt = 1; // these statements are executed many times, with // a_string representing a different value each time // and cnt growing by one with each execution ... cout << a_string << ( cnt % line_size ? ' ' : '\n' ); The parenthetical expression following the output operator likely makes no sense to you unless you are already familiar with the conditional operator (?:). The result of the expression is to output either a space or a newline character depending on whether the remainder operator evaluates to a zero or a nonzero value. Let's see what sense we can make of it. The expression cnt % line_size evaluates to zero whenever cnt is a multiple of line_size; otherwise, it evaluates to a nonzero value. Where does that get us? The conditional operator takes the following general form: expr ? execute_if_expr_is_true : execute_if_expr_is_false; If expr evaluates to true, the expression following the question mark is evaluated. If expr evaluates to false, the expression following the colon is evaluated. In our case, the evaluation is to feed either a space or a newline character to the output operator. A conditional expression is treated as evaluating to false if its value is zero. Any nonzero value is treated as true. In this example, whenever cnt is not a multiple of eight, the result is nonzero, the true branch of the conditional operator is evaluated, and a space is printed. A compound assignment operator provides a shorthand notation for applying an arithmetic operation on the object to be assigned. For example, rather than write cnt = cnt + 2; a C++ programmer typically writes cnt += 2; // add 2 to the current value of cnt A compound assignment operator is associated with each arithmetic operator: +=, -=, *=, /=, and %=. When an object is being added to or subtracted by 1, the C++ programmer uses the increment and decrement operators: cnt++; // add 1 to the current value of cnt cnt--; // subtract 1 from the current value of cnt There is a prefix and postfix version of the increment and decrement operators. In the prefix application, the object is either incremented (or decremented) by 1 before the object's value is accessed: int tries = 0; cout << "Are you ready for try #" << ++tries << "?\n"; In this example, tries is incremented by 1 before its value is printed. In the postfix application, the object's value is first used in the expression, and then incremented (or decremented) by 1: int tries = 1; cout << "Are you ready for try #" << tries++ << "?\n"; In this example, the value of tries is printed before it is incremented by 1. In both examples, the value 1 is printed. Each of the relational operators evaluates to either true or false. They consist of the following six operators: == equality a == b != inequality a != b < less than a < b > greater than a > b <= less than or equal a <= b >= greater than or equal a >= b Here is how we might use the equality operator to test the user's response: bool usr_more = true; char usr_rsp; // ask the user if she wishes to continue // read the response into usr_rsp if ( usr_rsp == 'N' ) usr_more = false; The if statement conditionally executes the statement following it if the expression within parentheses evaluates to true. In this example, usr_more is set to false if usr_rsp is equal to 'N'. If usr_rsp is not equal to 'N', nothing is done. The reverse logic using the inequality operator looks like this: if ( usr_rsp != 'Y' ) usr_more = false; The problem with testing usr_rsp only for 'N' is that the user might enter a lowercase 'n'. We must recognize both. One strategy is to add an else clause: if ( usr_rsp == 'N' ) usr_more = false; else if ( usr_rsp == 'n' ) usr_more = false; If usr_rsp is equal to 'N', usr_more is set to false and nothing more is done. If it is not equal to 'N', the else clause is evaluated. If usr_rsp is equal to 'n', usr_more is set to false. If usr_rsp is not equal to either, usr_more is not assigned. A common beginner programmer error is to use the assignment operator for the equality test, as in the following: // oops this assigns usr_rsp the literal character 'N' // and therefore always evaluates as true if ( usr_rsp = 'N' ) // ... The logical OR operator (||) provides an alternative way of testing the truth condition of multiple expressions: if ( usr_rsp == 'N' || usr_rsp == 'n' ) usr_more = false; The logical OR operator evaluates as true if either of its expressions evaluates as true. The leftmost expression is evaluated first. If it is true, the remaining expression is not evaluated. In our example, usr_rsp is tested for equality with 'n' only if it is not equal to 'N'. The logical AND operator (&&) evaluates as true only if both its expressions evaluate as true. For example, if ( password && validate( password ) && ( acct = retrieve_acct_info(password) )) // process account ... The topmost expression is evaluated first. If it evaluates as false, the AND operator evaluates as false; the remaining expressions are not evaluated. In this example, the account information is retrieved only if the password is set and is determined to be valid. The logical NOT operator (!) evaluates as true if the expression it is applied to is false. For example, rather than write if ( usr_more == false ) cout << "Your score for this session is " << usr_score << " Bye!\n"; we can write if ( ! usr_more ) ... Operator PrecedenceThere is one ''gotcha'' to the use of the built-in operators: When multiple operators are combined in a single expression, the order of expression evaluation is determined by a predefined precedence level for each operator. For example, the result of 5+2*10 is always 25 and never 70 because the multiplication operator has a higher precedence level than that of addition; as a result, 2 is always multiplied by 10 before the addition of 5. We can override the built-in precedence level by placing parentheses around the operators we wish to be evaluated first. (5+2)*10, for example, evaluates to 70. For the operators I've introduced, the precedence order is listed next. An operator has a higher precedence than an operator under it. Operators on the same line have equal precedence. In these cases, the order of evaluation is left to right. logical NOT arithmetic ( *, /, % ) arithmetic ( +, - ) relational ( <, >, <=, >= ) relational ( ==, != ) logical AND logical OR assignment For example, to determine whether ival is an even number, we might write ! ival % 2 // not quite right Our intention is to test the result of the remainder operator. If ival is even, the result is zero and the logical NOT operator evaluates to true; otherwise, the result is nonzero, and the logical NOT operator evaluates to false. Or at least that is our intention. Unfortunately, the result of our expression is quite different. Our expression always evaluates to false except when ival is 0! The higher precedence of the logical NOT operator causes it to be evaluated first. It is applied to ival. If ival is nonzero, the result is false; otherwise, the result is true. The result value then becomes the left operand of the remainder operator. false is made into 0 when used in an arithmetic expression; true is made into 1. Under default precedence, the expression becomes 0%2 for all values of ival except 0. Although this is not what we intended, it is also not an error, or at least not a language error. It is an incorrect representation only of our intended program logic. And the compiler cannot know that. Precedence is one of the things that makes C++ programming complicated. To correctly evaluate this expression, we must make the evaluation order explicit using parentheses: ! ( ival % 2 ) // ok To avoid this problem, you must hunker down and become familiar with C++ operator precedence. I'm not helping you in the sense that this section does not present either the full set of operators or a full treatment of precedence. This should be enough, though, to get you started. For the complete presentation, check out either Chapter 4 of [LIPPMAN98] or Chapter 6 of [STROUSTRUP97]. |
I l@ve RuBoard |
![]() ![]() |