CHAPTER 3 Types, Operators, and Expressions; While and For Reference: Brooks, Chapter 3 (3.2 - 3.3) It is possible to mix data types in arithmetic expressions in C The following example is perfectly legal in C /* * File : main.c * Author: Robert C. Carden IV */ #include <stdio.h> void main(void) { int hours; double speed, distance; printf("Enter the number of hours: "); scanf("%d", &hours); printf("Enter the speed in MPH: ", &speed); scanf("%lf", &speed); distance = hours * speed; printf("You travelled %.2f miles " "at %.2f MPH in %d hours\n", distance, speed, hours); } In this example, we are multiplying an integer with a double C mandates that all expressions be computed using operand of the same data type In this example, C has rules for converting one or both numerical operands to a common type Since double is considered to be a higher type than int, the integer operand is converted (promoted) to double and double multiplication is performed Copyright (c) 1999 by Robert C. Carden IV, Ph.D. 2/6/2016 Types, Operators, and Expressions Implicit type conversions Performed when an operator has operands of different types The operands are converted to a common type General rules In general, the only automatic conversions are those that convert a narrower operand into a wider one without losing information. Assignments of a wider type to a narrower type loses information but is allowed. A char is treated as a small integer. They may be used freely within arithmetic expressions. The following example shows how characters and integers are used freely within expressions. /* * ctoi: if ch is a numerical character, return * the numerical equivalent * if ch is not, return -1 */ int ctoi(char ch) { int digit = -1; if (ch >= '0' && ch <= '9') { /*compute integer value*/ digit = ch - '0'; } return(digit); } 3-2 Types, Operators, and Expressions Implicit type conversions -- example /** ** lower: convert c to lower case (ASCII only) ** ** For portability reasons, we pass in ** characters as integers. See later ** discussion on this point. **/ int lower(int c) { if (c >= 'A' && c <= 'Z') /* * uppercase letter -- convert it * to lower case */ return c + 'a' - 'A'; else /* not an uppercase letter */ return c; } This function works for ASCII but fails for EBCDIC. In ASCII, corresponding upper and lower case letters are a fixed distance apart as numeric values Letters are also contiguous 3-3 Types, Operators, and Expressions Portable character functions and macros example functions isdigit(int c) isupper(int c) toupper(int c) tolower(int c) semantics of function or macro returns 1 <=> c is a digit returns 1 <=> c is uppercase converts c to uppercase converts c to lowercase To overcome this common problem, the header <ctype.h> is available (and required under ANSI). This header defines a family of functions and macros for doing character tests and conversions that are independent of the character set Each installation will provide its own <ctype.h> to do whatever is necessary Now we can rewrite our lower function using these macros: #include <ctype.h> int lower(int c) { if ( isupper(c) ) return tolower(c) else return c; } The C language does not specify whether variables of type char are signed or unsigned quantities. The C definition guarantees that any character in a machine's standard character set will never be negative. For portability, if you use characters as integers to store non-character data, specify whether it is signed or unsigned. When passing or receiving characters to and from functions, people often use int to store characters. Problems sometimes arise when users must be able to store an arbitrary character and EOF (-1) in a character variable. In the EBCDIC character set, it already needs all 256 possible values but adding EOF requires 257. 3-4 Types, Operators, and Expressions Type conversions -- A.6, p.198, K&R The following conversions are performed in an arithmetic expression of mixed types If either operand is long double, the other is converted to long double Otherwise, if either is double, convert the other to double Otherwise, if either is float, convert the other to float [Integral promotions] Otherwise if either is unsigned long int, convert the other to unsigned long int Otherwise, if one operand is long int and the other is unsigned int, the effect depends on whether long int can represent all values of an unsigned int; if so, the unsigned int operand is converted to long int; if not, both are converted to unsigned long int Otherwise, if one operand is long int, convert the other to long int Otherwise, if either operand is unsigned int, convert the other to unsigned int Otherwise both operands have type int, i.e. smaller operands are promoted to int. Under ANSI, unsigned-ness of operands is not propagated. 3-5 Types, Operators, and Expressions Type conversion - the distance program void main(void) { int hours; double speed, distance; printf("Enter the number of hours: "); scanf("%d", &hours); printf("Enter the speed in MPH: ", &speed); scanf("%lf", &speed); distance = hours * speed; printf("You travelled %.2f miles " "at %.2f MPH in %d hours\n", distance, speed, hours); } The mixed mode expression in question is hours * speed Variable hours is of type int Variable speed is of type double Because they are of different types, an arithmetic type conversion must be applied 1. Neither variable is of type long double so that rule does not apply 2. Variable speed is of type double and since hours is not, hours shall be promoted to double and double (floating point) multiplication shall be performed No information is lost here because of the nature of double Note from earlier discussions that double has a 53 bit mantissa (on our Intel systems) By the same token, int is 32 bits and can easily fit into the integer part of the double The result of this expression is a double and the variable it is being assigned to is double These are the same so no conversion for the assignment is required 3-6 Types, Operators, and Expressions Type conversion - the distance program using floats void main(void) { int hours; float speed, distance; printf("Enter the number of hours: "); scanf("%d", &hours); printf("Enter the speed in MPH: ", &speed); scanf("%lf", &speed); distance = hours * speed; printf("You travelled %.2f miles " "at %.2f MPH in %d hours\n", distance, speed, hours); } The mixed mode expression in question is hours * speed Variable hours is of type int Variable speed is of type float Because they are of different types, an arithmetic type conversion must be applied 1. Neither variable is of type long double so that rule does not apply 2. Neither variable is of type double so that rule does not apply 3. Variable speed is of type float and since hours is not, hours shall be promoted to float and float (floating point) multiplication shall be performed In this example, information might be lost if hours is a fairly large integer Note from earlier discussions that float has a 24 bit mantissa (on our Intel systems) By the same token, int is 32 bits and may not be able to fit into the integer part of the float The excess, low order digits in the floating point number will be truncated The result of this expression is a float and the variable it is being assigned to is float These are the same so no conversion for the assignment is required 3-7 Types, Operators, and Expressions Integer constants revisited - some subtle points The type of integer constant is the first of the corresponding list in which its value can be represented (ANSI draft, section 3.1.3.2) unsuffixed decimal: unsuffixed octal or hexadecimal: suffixed by letter u or U: suffixed by letter l or L: suffixed by both u or U and l or L: int, long, unsigned long int, long, unsigned long unsigned int, unsigned long long, unsigned long unsigned long In LIMITS.H, we see: #define LONG_MIN #define LONG_MAX #define ULONG_MAX (-2147483647L - 1) 2147483647L 0xffffffffUL Consider the rather arcane definition of LONG_MIN Suppose we had rewritten it as #define LONG_MIN -2147483648L This would not work at all First, the compiler would determine the type of the constant 2147483648L Because it is suffixed with an L, the compiler may choose the first of long and unsigned long which can represent this integer However, this integer requires a full 32 bits and cannot be represented as a long Therefore, it must be an unsigned long Then, we negate the unsigned long 2147483648 That will produce another unsigned long and not the minimum signed long number 3-8 Types, Operators, and Expressions Mixed mode expressions - evaluating from the bottom up #include <stdio.h> void main(void) { double result1, result2; result1 = 2.56 + (3 / 2); result2 = 2.56 + (3.0 / 2); printf("result1 = 2.56+(3 /2) = %.2f\n", result1); printf("result2 = 2.56+(3.0/2) = %.2f\n", result2); } The example above illustrates a very common pitfall in C Many people are aware that integer division truncates and would expect 3/2 to produce a result of 1 Even more people, though, get confused when they cleverly observe that the result is being assigned to a double and therefore conclude that the entire expression is a double expression. Wrong. C evaluates expressions from the bottom up and determines the type and value of each subexpression as it goes, each step of the way In the case of computing result1, C first considers 3/2 Both operands are integers, therefore we do integer division and note that the result is an int Notice at this point there is no concern or care about the larger expression Notice that C does not care about the context Then C considers adding 2.56 to the integer result of computing 3/2 Now we are adding a double to an int, the int result is promoted to double, and the result is double Finally, we have a double which we are assigning to a double No conversion is necessary and the C compiler is happy with life 3-9 Types, Operators, and Expressions Mixed mode expressions - evaluating from the bottom up #include <stdio.h> void main(void) { double result1, result2; result1 = 2.56 + (3 / 2); result2 = 2.56 + (3.0 / 2); printf("result1 = 2.56+(3 /2) = %.2f\n", result1); printf("result2 = 2.56+(3.0/2) = %.2f\n", result2); } Here is the actual result of running this program: 3-10 Types, Operators, and Expressions Explicit type conversion -- assignments The expression x = y forces y to be converted into the type on the left Consider the following C fragment: float int i; f; /* converts int to float */ f = i; /* * converts float to int, * truncating fractional part */ i = f; Converting float to int causes the truncation of the fractional part Longer integers are converted to shorter ones or to chars by dropping the excess high-order bits. int char i; c; i = c; /* widen c to fit in i */ c = i; /* value originally in c is unchanged */ [Instructor: give examples on the board of these scenarios] 3-11 Types, Operators, and Expressions Explicit type conversions -- coerced The cast operator is of the form ( <type-name> ) It is a unary operator with the effect that it converts its operand to the desired type. The cast operator is an operator equal in precedence to unary minus (-) float f; int i, j; f = i / j; /* integer division */ f = (float) i / j; /* real division */ i = f % j; i = (int) f % j; /* syntax error */ /* f is converted to int */ A cast is an expression: it involves a cast operator and an operand to be cast It produces a value it does not modify the operand In the first example, it converts the int i to float The result of this is divided by j using floating point division In the second example, it converts the float back to int, truncating the fractional part That integer result is then modulo with j 3-12 Types, Operators, and Expressions Using casts to get the correct type of expression #include <stdio.h> void main(void) { result1 = 2.56 + (double) 3 / 2; result2 = 2.56 + (double)(3 / 2); printf("result1 = 2.56+(double)3 /2 = %.2f\n", result1); printf("result2 = 2.56+(double)(3/2) = %.2f\n", result2); } It is important to understand why the computation of result1 uses floating point division while the computation of result2 uses integer division Once again, all expressions are evaluated from the simplest, innermost expression outward 3-13 Types, Operators, and Expressions Increment and decrement operators Unary operators ++ and -- increment and decrement their respective operands highest precedence (same as unary -) right to left associativity Operand y = ++ x; y = x ++; y = -- x; y = x --; z++; x y y x x y y x z Equivalent C code = x + 1; /*increment*/ = x; /*use result*/ = x; /*use value*/ = x + 1; /*increment*/ = x - 1; = x; = x; = x - 1; = z + 1; When ++ or -- are placed before the operand, they are called preincrement and pre-decrement respectively. the return value of the expression is the new value of the operand When ++ or -- are placed after the operand, they are called postincrement and post-decrement respectively the return value of the expression is the original value of the operand Cannot apply ++ or -- to something cannot be an lvalue An lvalue is anything that can be updated, for instance on the left hand side of an assignment statement The expression (a+b)++ is illegal. However, the expression x[i]++ is legal and equivalent to x[i] = x[i] + 1 3-14 Types, Operators, and Expressions ANSI Note (p. 39) "Between the previous and next sequence point, an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored." Thus, if a variable is updated more than once within an expression between two consecutive sequence points, the behavior is undefined. example i = 1; i = ++i + 1; What gets assigned to i? = i tmp2 <-- tmp1 + 1 schedule store tmp2 to i + tmp1 <-- i+1 schedule store tmp1 to i (pre)++ 1 i Two assignments are being requested, but ANSI specifies that i shall have its stored value modified at most once. The resulting value of ++i will propagate through the expression, but the store may or may not occur. Consequently, the compiler may choose to allow the ++i to actually store into i or let the assignment statement store into i. Thus, only one update to i will win and thus i may be either 2 or 3. 3-15 Types, Operators, and Expressions Example expressions In the table below, assume that the following declarations are in scope. int a = 1, b = 2, c = 3, d = 4; The following table gives an expression, the equivalent fully parenthesized expression, the value of the expression, and then the subsequent values of a, b, c, and d after its evaluation. Assume that these expressions are evaluated independent of each other. Expression a * b / c a * b % c + a ++ a * b - c -7 - - b * ++ d Equivalent Expr (a * b) / c ((a * b) % c) + a ((++a) * b) - (c--) 7 - ((-b) * (++d)) Value 0 3 1 17 a 1 1 2 1 b 2 2 2 2 Illustration of third row tmp2 = tmp1*b - tmp3 = c schedule store of c-1 to c tmp1 = a+1 schedule store of tmp1 to a * (pre)++ (post)-- b a 3-16 c c 3 3 2 3 d 4 4 4 5 Types, Operators, and Expressions Assignment operators and expressions In C, any expression can be a statement. In Pascal, one uses an assignment statement. In C, assignments are simply expressions. As a result, it is perfectly legal to write the following meaningless code in C. 3; /* valid */ 4+5; /* valid */ Assignments are expressions The return value of an assignment expression is the value that just got assigned The object getting updated by the assignment is called an lvalue The object (expression) being assigned to it is called an rvalue example x = 5 + 7; In this example, x is the lvalue, 5+7 is the rvalue, and the return value of the entire expression is 12. 3-17 Types, Operators, and Expressions Example of assignments being expressions The following three statements b = 2; c = 3; a = b + c; may be written as one statement a = (b = 2) + (c = 3); = schedule store tmp3 in a tmp3 <-- tmp1 + tmp2 + a tmp1 <-- 2 schedule store tmp1 in b b = = 2 c 3-18 tmp2 <-- 3 schedule store tmp2 in c 3 Types, Operators, and Expressions example Assignment operators associate right to left Thus, the following expression a = b = c = 0; is equivalent to writing a = (b = (c = 0)); which in turn is equivalent to writing c = 0; b = 0; a = 0; Note that because assignment statements associate right to left, variables a, b, and c are all lvalues. C also provides operators such as += and -= to write expressions such as a += 2 instead of a = a + 2 In Algol, the equivalent expression would be a :=*+ 2 The following list contains all the possible assignment operators: = += -= *= /= %= 3-19 >>= <<= &= ^= |= Types, Operators, and Expressions Assignment operators The semantics of a general assignment operator is specified by variable op= expression being equivalent to variable = variable op (expression) In the table below, assume that the following declarations are in scope. int i = 1, j = 2, k = 3, m = 4; The following table gives an expression, the equivalent fully parenthesized expression, and the value of the expression. Assume that these expressions are evaluated independent of each other. Expression j *= k + 3 i += j + k j *= k = m + 5 Equivalent Expr j *= (k + 3) i += (j + k) j *= (k=(m+5)) 3-20 Equivalent Expr j = j * (k + 3) i = i + (j + k) j = j * (k = (m + 5)) Value 12 6 18 Types, Operators, and Expressions example The expression x *= 5; is equivalent to writing x = x * 5; Similarly, the expression y = (x1 += 1) * (x2 -= 2); is roughly equivalent to writing x1 = x1 + 1; x2 = x2 - 2; y = x1 * x2; Note, however, that because C does not enforce the order of evaluation of the multiplicative operands, the first and second statements may be interchanged example -- computing the powers of two /* some powers of two are printed */ #include <stdio.h> int main (void) { int power = 1; printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d", power *= 2); printf ("%6d\n", power *= 2); } The output of this program is 2 4 8 16 32 64 128 3-21 Types, Operators, and Expressions Conditional expressions Reference: Kelley & Pohl, Chapter 4 (4.17) Consider the following statement that sets z to the max of a and b: if (a > b) z = a; else z = b; The conditional expression operator may be used instead. The ternary operator ?: may be used thus: expr1 ? expr2 : expr3 Thus, our example may be rewritten: z = (a > b) ? a : b; The right hand side is an expression which has the value of max(a,b). In fact, the ternary expression is often used in macros, e.g., #define MAX(A,B) (((A) > (B)) ? (A) : (B)) Also, because ?: is an expression, if expr2 or expr3 are of different types, the result is an expression of the highest type. This activity is often called “type promotion.” That is, in the code fragment: Double d = 10.23; Float f = 3.1416; int i = 10; ... x = (d > 0) ? f : i; the resulting type of the right-hand side of the assignment to x is float because a mixed expression with float and int becomes float (expr2 is float, expr3 is int). The precedence of ?: is just above the assignment operators. It associates right to left. 3-22 Types, Operators, and Expressions The conditional operator Recall that the conditional operator ?: has the following syntax: conditional_expression expr1 expr2 expr3 ::= ::= ::= ::= expr1 ? expr2 : expr3 expression expression expression Also, recall that this operator has the semantics that expr1 is evaluated first. Then if expr1 is nonzero (true), expr2 is evaluated and that is the value of the conditional expression as a whole. Otherwise, expr1 is zero (false), and expr3 is evaluated making it the value of the conditional expression as a whole. Consequently, the conditional operator can be used to perform the work of an if-else statement. Note that this can quickly get out of hand if you’re not careful… example if-else construct if (y < x = else x = if (a > z = else if z = else z = if (a > if z) y; z; 0) 1; (a < 0) -1; 0; b) (a > c) z = a; else z = c; else if (b > c) z = b; else z = c; equivalent conditional x = (y < z) ? y : z; z = (a > 0) ? 1 : ((a < 0) ? -1 : 0); z = (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); 3-23 Types, Operators, and Expressions The conditional operator -- example In the following example, assume that the following declarations are in scope: char a = 'a'; /* a has an ASCII value of 97 */ char b = 'b'; /* b has an ASCII value of 98 */ int i = 1; int j = 2; double x = 7.07; Then, the following table illustrates the effect of the conditional operator. Exercise: explain why the first expression is an int rather than char Expression Equivalent Expr Value Type i==j ? a-1 : b+1 j%3==0 ? i+4 : x j%3 ? i+4 : x (i==j) ? (a-1) : (b+1) ((j%3)==0) ? (i+4) : x (j%3) != 0 ? (i+4) : x 99 7.07 5.0 int double double 3-24 Types, Operators, and Expressions Comma operator When used within an expression, the comma ',' operator, or sequencing operator, is defined thus: "A pair of expressions separated by a comma is evaluated left to right, and the type and value of the result are the type and value of the right operand." The comma operator has the lowest possible precedence (lower than assignment statements). The comma operator associates from left to right. f (a (), (b (), c ())); /* call f with two parameters */ A better way to do this might be the following: b (); f (a (), c ()); note The commas that separate function arguments, variables in declarations, etc., are not comma operators, and do not guarantee left to right evaluation. example The statement x = (i++, j++, k++); is exactly equivalent to writing i++; j++; x = k++; Comma operators are commonly used within for loops to initialize more than one looping index, e.g.: 3-25 Types, Operators, and Expressions for (i = 0, j = 1, k = 2; i < n; i++, j++, k++) { ... } 3-26 Types, Operators, and Expressions Precedence and order of evaluation Operator () ! ++ -(unary) + (indirection) * & ( type ) sizeof * / % + < <= >= > == != Associativity left to right Order of Evaluation - right to left - left to right left to right left to right left to right && left to right || left to right ?: right to left left to right sequence point after first argument short circuit left to right sequence point after first argument short circuit first operand eval sequence point after first argument = += -= *= /= %= right to left , left to right 3-27 left to right sequence point after first argument Types, Operators, and Expressions ANSI notes on the comma and other operators in C The ANSI draft, page 55, states the following about the comma operator: "The left operand of a comma operator is evaluated as a void expression; there is a sequence point after its evaluation." It is also important to note that the comma operator yields an expression and does not yield an lvalue. Thus, the following is incorrect: int k; int j; (j = 10, k = 20) = 40; In other places it states that as && and || guarantee left to right evaluation, there is also a sequence point after evaluating the first operand. In general, a semicolon ';' denotes a sequence point, i.e. it is the point at which everything must synch up. Up until that point, all subexpressions can be conceptually evaluated in parallel. Precedence, associativity, and order of evaluation Precedence, associativity, and order of evaluation are orthogonal concepts, e.g., 10 * 20 + 20 / 5 + 15 … ((10 * 20) + (20 / 5)) + 15 Most operators do not have an order of evaluation specified This “underspecification” allows compilers to optimize your code Only logical and '&&', logical or '||', the ternary operator '?:', and the comma ',' operator explicitly specify order of evaluation and define sequence points Also, note that the ternary operator associates right to left. 3-28 Types, Operators, and Expressions Order of evaluation -- pitfalls With the exception of &&, ||, ?:, and the comma operator, C does not specify the order in which operands of an operator are evaluated. example The code fragment x = f () + g (); may evaluate f() followed by g(), or it may choose to evaluate g() followed by f(). f() g() + assign to x Adding parentheses changes nothing, e.g.: x = (f ()) + g (); The only way to guarantee order of evaluation is to split this up into several statements x1 = f (); x2 = g (); x = x1 + x2; 3-29 Types, Operators, and Expressions Order of evaluation -- pitfalls (2) Similarly, the order in which arguments to a function are evaluated is not specified. printf ("%d %d\n", ++n, power (2, n)); /* wrong */ Different results will occur depending on whether ++n is evaluated before or after power(2,n) is evaluated. "%d %d\n" n 2 n fetch n (pre) ++ power schedule store of <n+1> to n printf Changing the ++n to n++ can still lead to unpredictable results The underlying problem is that we have a potential “race condition” Other types of race conditions occur in concurrent programs 3-30 Types, Operators, and Expressions The while statement Reference: Brooks, Chapter 4 (4.4) The syntax of a while statement is as follows. while_statement ::= While (expression) statement Generally, statement is a compound statement while condition false true statement1 statement2 ... statementN 3-31 Types, Operators, and Expressions The while statement (2) Some examples are int x int i while { x i } = 0; = 0; (i < n) = x + i; /* x++ */ = i + 1; /* i++ */ int c; while ((c = getchar ()) != EOF) { /* Only works for ASCII, better to use islower (c). */ if (c >= 'a' && c <= 'z') ++lowercase_letter_cnt; ++total_cnt; } but not while (++i < LIMIT) do { /* syntax error: do is not allowed */ j = 2 * i + 3; printf ("%d\n", j); } In general, given a sequence of statements of the form while (expression) statement next statement the first expression is evaluated. If it is nonzero (true), then statement is executed repeatedly until expression becomes zero (false). When expression becomes false, control is passed to next statement. 3-32 Types, Operators, and Expressions Example -- computing factorials #include <stdio.h> int main (void) { int i, n; double factorial; printf ("Enter a positive integer: "); scanf ("%d", &n); factorial = 1.0; i = 1; while (i <= n) { factorial = factorial * i; // factorial *= i; i = i + 1; // i++; } printf ("%d factorial = %.2f\n", n, factorial); return 0; } Whenever you write a while loop, be sure you determine how it will terminate A common error would be to omit the increment of i The result would be an endless loop Another common error is to forget to initialize i before the beginning of the loop In this case, the behavior is unpredicatable 3-33 Types, Operators, and Expressions Example -- computing factorials (2) The main code fragment factorial = 1.0; i = 1; while (i <= n) { factorial = factorial * i; i = i + 1 ; } may be rewritten more concisely as follows factorial = 1.0; i = 1; while (i <= n) factorial *= i++; but not as follows factorial = 1; i = 1; while (i < n) factorial *= i; The last example is an infinite loop. The looping variable i is never modified and thus never becomes >= n 3-34 Types, Operators, and Expressions Example - Fibonacci Numbers The Fibonacci sequence is a sequence of numbers where the current number is the sum of the previous two numbers in the sequence The first two numbers in the sequence is 1 The next number is 1 + 1 = 2 The next number after that is 1 + 2 = 3 Here are the first 10 numbers of the sequence: 1 1 2 3 5 8 13 21 34 55 Now we write code to generate N Fibonacci numbers and compute their sum prev1 = prev2 = 1; /* first two numbers */ sum = 2; /* sum of the first two numbers */ count = 2; /* calculate 3rd number on */ while (count < N) { int curr = prev1 + prev2; sum += curr; prev1 = prev2; /* advance prev1 */ prev2 = curr; /* advance prev2 */ count++; } If N=10, how many iterations does this loop execute? What is the final value of count when N=10? If N=5, what are the final values of curr, sum, prev1, and prev2? 3-35 Types, Operators, and Expressions example -- binary search /* binsearch: find x in v[0] <= ... <= v[n-1] -- return index of x in a sorted array of numbers */ int binsearch (int x, int v[], int n) { int low = 0; int high = n - 1; while (low <= high) { int mid = (low + high) / 2; if (x < v[mid]) /* smaller */ high = mid - 1; else if (x > v[mid]) /* larger */ low = mid + 1; else /* we found it */ return mid; /* return the index */ } return -1; /*NOT FOUND*/ } low mid-1 mid+1 mid exercise Identify the statements within each if or while construct. 3-36 high Types, Operators, and Expressions Example @@ Because C allows side effects within expressions, some or possibly all of the work may be performed as part of the test. The following example illustrates a case where all of the work is done during the test. #include <stdio.h> #include <ctype.h> int skip_spaces (void) { int c; while ((c = getchar ()) != EOF && isspace (c)) /* null */; return c; } The while loop read characters one at a time and stops either when it hits end-of-file or when the character is no longer a whitespace character. The null statement ; is the body of the loop. 3-37 Types, Operators, and Expressions The FOR statement (1) The syntax of a for statement is as follows. for ( initialization-expression ; condition ; next-expression ) statement next statement for initializationexpression condition false true statement1 statement2 ... statementN next-expression 3-38 Types, Operators, and Expressions Example -- computing factorials factorial = 1; i = 1; while (i <= n) { factorial *= i; i++; } factorial = 1; for (i = 1; i <= n; i++) { factorial *= i; } A common programming mistake when one uses a while construct is to either forget the initialization of the variables or to forget the increment of the loop index The for construct puts both of those things at the top Programmers are less likely to forget these things A for loop is simply a more “general” while loop for initializationexpression condition false true statement1 statement2 ... statementN next-expression 3-39 Types, Operators, and Expressions Example - Fibonacci Numbers Here is the version which uses a while loop prev1 = prev2 = 1; /*first two numbers*/ sum = 2; /*sum of the first two numbers*/ count = 2; /*calculate 3rd number on*/ while (count++ < N) { curr = prev1 + prev2; sum += curr; prev1 = prev2; /*advance prev1*/ prev2 = curr; /*advance prev2*/ } Here it is again, but using a for loop prev1 = prev2 = 1; /*first two numbers*/ for (sum = 2, count = 2; count < N; count++) { curr = prev1 + prev2; sum += curr; prev1 = prev2; /*advance prev1*/ prev2 = curr; /*advance prev2*/ } In this example, we use the comma operator to form a longer, more complex initialization expression We could also use it for create longer next expressions The logic of the for loop is slightly different here, but we also got rid of rather tricky code that the while loop was using The while version post-incremented a count whose initial value was 2 The number of iterations is N-2 The for version starts with count=3 and continues until it is N, thus iteration (N-3)+1 times, i.e. N+2 times 3-40 Types, Operators, and Expressions The FOR statement (2) The for statement, like the while statement, is used to execute code iteratively. The for statement in C is designed to be a lot more general (and potentially more powerful) than Algol-like languages. The following table gives syntax of various for-like constructs and the equivalent while construct. for statement syntax equivalent while syntax expr1 ; while (expr2) { statement expr3 ; } next statement while (expr2) { statement expr3 ; } next statement while (expr2) statement next statement while (1) { statement expr3 ; } next statement while (1) statement next statement for (expr1; expr2; expr3) statement next statement for ( ;expr2; expr3 ) statement next statement for ( ;expr2; ) statement next statement for ( ;;expr3 ) statement next statement for (;;) statement next statement 3-41 Types, Operators, and Expressions The FOR statement (3) More generally, the syntax is a follows: for_statement expr1 expr2 expr3 ::= ::= | ::= | ::= | for (expr1 ; expr2 ; expr3) statement initialization-expression test-relation-expression next-expression The first optional expression within the for loop is the initialization It is done before the loop starts The second optional expression is the test condition It is performed at the beginning of each iteration If it is nonzero (true), the body of the loop is performed If it is zero (false), the for loop terminates The third optional expression is an expression which will be performed as the last statement in the loop In this context, the semicolons are expression separators 3-42 Types, Operators, and Expressions The FOR statement (4) example -- infinite loop for ( ;; ) /* while (1) */ { statements } example for construct int i, n, sum; ... sum = 0; for (i=1; i<=n; i++) sum = sum + i; expr1: expr2: expr3: equivalent while construct int i, n, sum; ... sum = 0; i = 1; while (i <= n) { sum = sum + i; i++; } i = 1 i <= n i++ 3-43 Types, Operators, and Expressions The FOR statement (5) The for statement is similar to the Pascal for loop or the Fortran DO statement However, in C, both the index and limit can be altered within the loop example int getlimit (int limit); int getindex (int index); void ugly (void) { int i = 0, limit = 100; for (i = 0; i < limit; i++) { i = getindex (i); limit = getlimit (limit); printf ("Hmmm: i=%d and limit=%d\n", i, limit); } } This type of thing is not a good idea… The loop index and limit are changed by an outsider The behavior can be very unpredictable 3-44