.NH If; relational operators; compound statements .PP The basic conditional-testing statement in C is the .UL if statement: .E1 c = getchar( ); if( c \*= '?' ) printf("why did you type a question mark?\\n"); .E2 The simplest form of .UL if is .E1 if (expression) statement .E2 .PP The condition to be tested is any expression enclosed in parentheses. It is followed by a statement. The expression is evaluated, and if its value is non-zero, the statement is executed. There's an optional .UL else clause, to be described soon. .PP The character sequence `==' is one of the relational operators in C; here is the complete set: .E1 \*= equal to (\*.EQ\*. to Fortraners) != not equal to > greater than < less than >= greater than or equal to <= less than or equal to .E2 .PP The value of .UL ``expression .UL relation .UL expression'' is 1 if the relation is true, and 0 if false. Don't forget that the equality test is `=='; a single `=' causes an assignment, not a test, and invariably leads to disaster. .PP Tests can be combined with the operators .UL `&&' .UC (AND), .UL `\*|' .UC (OR), and .UL `!' .UC (NOT). For example, we can test whether a character is blank or tab or newline with .E1 if( c\*=' ' \*| c\*='\\t' \*| c\*='\\n' ) \*.\*.\*. .E2 C guarantees that .UL `&&' and .UL `\*|' are evaluated left to right _ we shall soon see cases where this matters. .PP One of the nice things about C is that the .UL statement part of an .UL if can be made arbitrarily complicated by enclosing a set of statements in {}. As a simple example, suppose we want to ensure that .UL a is bigger than .UL b, as part of a sort routine. The interchange of .UL a and .UL b takes three statements in C, grouped together by {}: .E1 .ne 5 if (a < b) { t = a; a = b; b = t; } .E2 .PP As a general rule in C, anywhere you can use a simple statement, you can use any compound statement, which is just a number of simple or compound ones enclosed in {}. There is no semicolon after the } of a compound statement, but there .ul is a semicolon after the last non-compound statement inside the {}. .PP The ability to replace single statements by complex ones at will is one feature that makes C much more pleasant to use than Fortran. Logic (like the exchange in the previous example) which would require several GOTO's and labels in Fortran can and should be done in C without any, using compound statements. .NH While Statement; Assignment within an Expression; Null Statement .PP The basic looping mechanism in C is the .UL while statement. Here's a program that copies its input to its output a character at a time. Remember that `\\0' marks the end of file. .E1 main(~) { char c; while( (c=getchar(~)) != '\\0' ) putchar(c); } .E2 The .UL while statement is a loop, whose general form is .E1 while (expression) statement .E2 Its meaning is .E1 (a) evaluate the expression (b) if its value is true (i\*.e\*., not zero) do the statement, and go back to (a) .E2 Because the expression is tested before the statement is executed, the statement part can be executed zero times, which is often desirable. As in the .UL if statement, the expression and the statement can both be arbitrarily complicated, although we haven't seen that yet. Our example gets the character, assigns it to .UL c, and then tests if it's a `\\0''. If it is not a `\\0', the statement part of the .UL while is executed, printing the character. The .UL while then repeats. When the input character is finally a `\\0', the .UL while terminates, and so does .UL main\*. .PP Notice that we used an assignment statement .E1 c = getchar(~) .E2 within an expression. This is a handy notational shortcut which often produces clearer code. (In fact it is often the only way to write the code cleanly. As an exercise, re-write the file-copy without using an assignment inside an expression.) It works because an assignment statement has a value, just as any other expression does. Its value is the value of the right hand side. This also implies that we can use multiple assignments like .E1 x = y = z = 0; .E2 Evaluation goes from right to left. .PP By the way, the extra parentheses in the assignment statement within the conditional were really necessary: if we had said .E1 c = getchar(~) != '\\0' .E2 .UL c would be set to 0 or 1 depending on whether the character fetched was an end of file or not. This is because in the absence of parentheses the assignment operator `=' is evaluated after the relational operator `!='. When in doubt, or even if not, parenthesize. .PP Since .UL putchar(c) returns .UL c as its function value, we could also copy the input to the output by nesting the calls to .UL getchar and .UL putchar: .E1 main(~) { while( putchar(getchar(~)) != '\\0' ) ; } .E2 What statement is being repeated?~ None, or technically, the .ul null statement, because all the work is really done within the test part of the .UL while\*. This version is slightly different from the previous one, because the final `\\0' is copied to the output before we decide to stop. .NH Arithmetic .PP The arithmetic operators are the usual `+', `\(mi', `*', and `/' (truncating integer division if the operands are both .UL int), and the remainder or mod operator `%': .E1 x = a%b; .E2 sets .UL x to the remainder after .UL a is divided by .UL b (i.e., .UL a .UL mod .UL b)\*. The results are machine dependent unless .UL a and .UL b are both positive. .PP In arithmetic, .UL char variables can usually be treated like .UL int variables. Arithmetic on characters is quite legal, and often makes sense: .E1 c = c + 'A' - 'a'; .E2 converts a single lower case ascii character stored in .UL c to upper case, making use of the fact that corresponding ascii letters are a fixed distance apart. The rule governing this arithmetic is that all .UL chars are converted to .UL int before the arithmetic is done. Beware that conversion may involve sign-extension _ if the leftmost bit of a character is 1, the resulting integer might be negative. (This doesn't happen with genuine characters on any current machine.) .PP So to convert a file into lower case: .E1 main( ) { char c; while( (c=getchar( )) != '\\0' ) if( 'A'<=c && c<='Z' ) putchar(c+'a'-'A'); else putchar(c); } .E2 Characters have different sizes on different machines. Further, this code won't work on an IBM machine, because the letters in the ebcdic alphabet are not contiguous. .NH Else Clause; Conditional Expressions .PP We just used an .UL else after an .UL if\*. The most general form of .UL if is .E1 if (expression) statement1 else statement2 .E2 the .UL else part is optional, but often useful. The canonical example sets .UL x to the minimum of .UL a and .UL b: .E1 .ne 4 if (a < b) x = a; else x = b; .E2 Observe that there's a semicolon after .UL x=a\*. .PP C provides an alternate form of conditional which is often more concise. It is called the ``conditional expression'' because it is a conditional which actually has a value and can be used anywhere an expression can. The value of .E1 a