COP 3223H meeting -*- Outline -*- * Conditionals in C ** conditional expressions ------------------------------------------ CONDITIONAL EXPRESSIONS Purpose: to compute a value based on the program's state Syntax: ::= ? : Examples: (d != 0.0 ? n / d : 10.0) (n > 0 ? sqrt(n) : 0.0) Semantics: ------------------------------------------ The condition (before '?') is evaluated first, if the condition is true, then the value is the value of the then expression (after the '?' if), otherwise the value is the value of the else expression (after ':') I typically parenthesize the conditional expressions in C, as I think that makes the code clearer, but it's not required. ------------------------------------------ &&, || ARE SYNTACTIC SUGARS E1 && E2 ==> E1 || E2 ==> So "&&", "||" are short-circuit operators, the only evaluate their second argument if necessary Examples: d!=0 && n/d > 3.0 n<0 || sqrt(n) < 100.0 ------------------------------------------ ... (E1 ? E2 : false) ... (E1 ? true : E2) However, these desugarings are a bit of a lie, since e1 is not evaluated twice as it would appear Q: How is this different than the Python conditional expressions? order is different, uses ? and : instead of if and else. ** if-statements ------------------------------------------ IF-STATEMENTS Purpose: to control what actions are executed based on the program's state. Syntax: ::= ... | ::= ... | { } ::= | | Examples: if (i < len) { sum = sum + i; } if (i < len) { sum = sum + i } else { ; } if (num == 1) { return "one"; } else if (num == 2) { return "two"; } else if (num == 3) { return "three"; } else { return "more"; } Semantics: evaluates the first , ------------------------------------------ ... if ( ) | if ( ) else Note that the parentheses are part of the syntax. is the syntax for declarations, which have the scope of the We recommend that the always be a compound-stmt, { S... }, although that is not technically necessary advantages: the parts are clearer, the scopes match the syntax more clearly (each sub-statement is a scope) adding or subtracting statements is easier ... if that is not zero, then execute the first otherwise execute the else if any. Q: How is the if-statement in C different than the if-statement in Python? Syntax is different ------------------------------------------ FOR YOU TO DO Write a function int max(int x, int y) that returns the larger of the ints x and y passed as arguments, so that if after res = max(x,y) the following holds: res >= x and res >= y. ------------------------------------------ ... // Ensures: result is the least upper bound of x and y int max(int x, int y) { if (x >= y) { return x; } else { return y; } } ** switch-statements ------------------------------------------ SWITCH STATEMENTS Purpose: Syntax: ::= ... | switch | case | break; | default ::= // known at compile time integer constant expressions can be: integer literals, e.g., 3, 9, 'c', sizeof expressions, e.g., sizeof(buf) a macro that is an integer constant, e.g., NULL, true ... ------------------------------------------ ... to quickly (efficiently) jump to one of a number of alternative actions based on an integer ... switch ( ) | case : | break; | default : other integer constants can be address constants, but we ignore that for now ------------------------------------------ SEMANTICS Semantics: case N: and default: are break; Usual case of a switch is something like: switch (E) { case N1: case N2: S12 break; case N3: S3 break; default: S4 } which is equivalent to: ------------------------------------------ ... labels for the following statment, which don't affect control flow themselves, but give jump targets for a surrounding switch ... finishes execution of the closest (lexically) surrounding switch (or while or for) statement and continues with the next statement ... is equivalent to: { int _ev = E; // _ev is fresh if (_ev == N1 || ev == N2) { S12 } else if (_ev == N3) { S3 } else { S4 } } Note: can't use switch for conditions that are different from integer equality. However, the compiled code is usually implemented with an indirect jump, with all the case-labeled statements in a table and the jump indexing (based on the expression, E) a row of the table and jumping to the corresponding label's address. Using switch in C is essentially telling the compiler to make such a jump table and to compile an indirect jump instruction. ------------------------------------------ EXAMPLE switch (num) { case 1: res = "one"; break; case 2: res = "two"; break; case 3: res = "three"; break; default: res = "more"; break; } return res; ------------------------------------------ Q: How does this relate to the if-statment we wrote earlier? *** fall though of cases It was originally thought to be useful to omit the break statement at the end of a case clause, which allows code for several cases to build on each other, but for most programmers this is usually just a confusing error ... ------------------------------------------ BEWARE: CASES MAY FALL THROUGH! By default, code for a case may continue into the next case(s). What is the final value of res when num == 1? switch (num) { case 1: res = "one"; case 2: res = "two"; case 3: res = "three"; default: res = "more"; } Moral: Always use break; at the end of each case! Think of the syntax as: ::= ... | ::= { } ::= | ::= break; ::= case : | case : ::= default: break; | default: // at the end ------------------------------------------ Clearly document any exceptions to the moral in the code *** example ------------------------------------------ FOR YOU TO DO Using a switch statment, write a function: int parse_answer(char c) that returns 0 if c is 'n' or 'N' 1 if c is 'y' or 'Y' 2 otherwise ------------------------------------------