CS13002 Programming and Data Structures | Spring semester |
Conditions and branching
Now we will break our impasse of monolithically executing statements after statements from top to bottom. We add jumps inside the program. We still continue with the basic top-to-bottom flow, but will now allow leaving out some sections conditionally.
Think about mathematical definitions like the following. Suppose we want to assign to y the absolute value of an integer (or real number) x. Mathematically, we can express this idea as:
0 if x = 0, y = x if x > 0, -x if x < 0.From a programmer's point of view this means that if x = 0, we can blindly assign to y the constant 0. If x is non-zero but positive, we can simply copy x to y. Finally, if x is negative, we have to take the unary minus of it and assign that negated value to y. In other words, depending on the value of x we have to do different things. At a particular time, x can have only one value and exactly one of the three possibilities need be executed. However, at different times x may have different values, and so our program should be able to handle all possibilities. This exemplifies what is called a selective structure in a program.
As another example, let us define the famous Fibonacci numbers:
0 if n = 0, Fn = 1 if n = 1, Fn-1 + Fn-2 if n >= 2.Now you are again in a similar situation. Depending on the value of n, you have three options. If n = 0 or 1, you immediately know what the corresponding Fibonacci number is. If n is bigger than 1, you compute the two previous Fibonacci numbers and add them up. How to compute the previous two numbers? Once again you check which of the three given conditions hold. And then repeat. Right now we will not study repetitive structures, but only mention that the possibility of repetition is dictated by the value of n.
If your program has to work in such a conditional world, you need two constructs:
- A way to specify conditions (like x < 0, or n >= 2).
- A way to selectively choose different blocks of statements depending on the outcomes of the condition checks.
Logical conditions Let us first look at the rendering of logical conditions in C. A logical condition evaluates to a Boolean value, i.e., either "true" or "false". For example, if the variable x stores the value 15, then the logical condition x > 10 is true, whereas the logical condition x > 100 is false.
Comparing two variables
The usual mathematical relations comparing two expressions E1 and E2 can be implemented in C as the following table illustrates:
Relational operator Usage Condition is true iff == E1 == E2 E1 and E2 evaluate to the same value != E1 != E2 E1 and E2 evaluate to different values < E1 < E2 E1 evaluates to a value smaller than E2 <= E1 <= E2 E1 evaluates to a value smaller than or equal to E2 > E1 > E2 E1 evaluates to a value larger than E2 >= E1 >= E2 E1 evaluates to a value larger than or equal to E2 The equality checker is == and not the single =. Recall that = is the assignment operator. In a place where a logical condition is expected, an assignment of the form E1 = E2 makes sense and could be a potential source of bugs.
Example: Let x and y be integer variables holding the values 15 and 40 at a certain point in time. At that time, the following truth values hold:
x == y False x != y True y % x == 10 True 600 < x * y False 600 <= x * y True 'B' > 'A' True x / 0.3 == 50 False (due to floating point errors)What is Boolean value in C?
A funny thing about C is that it does not support any Boolean data type. Instead it uses any value (integer, floating point, character, etc.) as a Boolean value. Any non-zero value of an expression evaluates to "true", and the zero value evaluates to "false". In fact, C allows expressions as logical conditions.
Example:
0 False 1 True 6 - 2 * 3 False (6 - 2) * 3 True 0.0075 True 0e10 False 'A' True '\0' False x = 0 False x = 1 TrueThe last two examples point out the potential danger of mistakenly writing = in place of ==. Recall that an assignment returns a value, which is the value that is assigned.
Logical operators
Logical operators are used to combine multiple logical conditions. In the following table C, C1 and C2 are assumed to be logical conditions (including expressions).
Logical operator Syntax True if and only if AND C1 && C2 Both C1 and C2 are true OR C1 || C2 Either C1 or C2 or both are true NOT !C C is false Example:
(7*7 < 50) && (50 < 8*8) True (7*7 < 50) && (8*8 < 50) False (7*7 < 50) || (8*8 < 50) True !(8*8 < 50) True ('A' > 'B') || ('a' > 'b') False ('A' > 'B') || ('A' < 'B') True ('A' < 'B') && !('a' > 'b') TrueNotice that here is yet another source of logical bug. Using a single & and | in order to denote a logical operator actually means letting the program perform a bit-wise operation and possibly ending up in a logically incorrect answer.
Let us now review the question of precedence and associativity of relational and logical operators. The following table summarizes the relevant details with precedence decreasing downwards.
Operator(s) Type Associativity ! Unary Right < <= > >= Binary Left == != Binary Left && Binary Left || Binary Left Example:
x <= y && y <= z || a >= b is equivalent to ((x <= y) && (y <= z)) || (a >= b). C1 && C2 && C3 is equivalent to (C1 && C2) && C3. a > b > c is equivalent to (a > b) > c.Let us now see how we can use conditions to write selective structures in C.
The if statement
Imagine a situation like this:
Do PDS lab; Have snacks; If it does not rain, play soccer; Solve Maths assignment; Enjoy dinner;In this example, playing soccer is dependent on rain. If it is not rainy, play soccer, else skip it and continue with your remaining pending work. A situation like this is described in the following figure:
This kind of structure can be rendered in C as follows:
if (Condition) { Block 1 }Here "block" means a sequence of statements. If the block consists of a single statement, the braces may be omitted.
Suppose you scan an integer x from the user and then replace it with its absolute value. if x is bigger than or equal to 0, there is nothing to do. If x is negative, replace it by -x.
scanf("%d",&x); if (x < 0) x = -x;
Animation example : one-way branching
The if-else statement
Now suppose you are adamant to play something after your PDS lab.
Do PDS lab; Have snacks; If it does not rain, play soccer, otherwise play table tennis; Solve Maths assignment; Enjoy dinner;This is an example of a situation depicted in the figure below:
This kind of structure can be rendered in C as follows:
if (Condition) { Block 1 } else { Block 2 }If a block consists of a single statement, the corresponding braces may be omitted.
Suppose you scan an integer x from the user and assign to y the absolute value of x. if x is bigger than or equal to 0, then simply copy x to y. If x is negative, copy -x to y.
scanf("%d",&x); if (x >= 0) y = x; else y = -x;
Animation example : two-way branching
Interactive animation : two-way branching
Consider the following special form of the if-else statement:
if (C) v = E1; else v = E2;Here depending upon the condition C, the variable v is assigned the value of either the expression E1 or the expression E2. This can be alternatively described as:
v = (C) ? E1 : E2;Here is an explicit example. Suppose we want to compute the larger of two numbers x and y and store the result in z. We can write:
z = (x >= y) ? x : y;Nested if statements
A block of an if or if-else statement may itself contain one or more if and/or if-else statements. Suppose that we want to compute the absolute value |xy| of the product of two integers x and y and store the value in z. Here is a possible way of doing it:
if (x >= 0) { z = x; if (y >= 0) z *= y; else z *= -y; } else { z = -x; if (y >= 0) z *= y; else z *= -y; }This can also be implemented as:
if (x >= 0) z = x; else z = -x; if (y >= 0) z *= y; else z *= -y;Here is a third way of doing the same:
if ( ((x >= 0)&&(y >= 0)) || ((x < 0)&&(y < 0)) ) z = x * y; else z = -x * y;
Animation example : nested branching
Interactive animation : nested branching
Interactive animation : max of three elements
Multi-way branching Now think of your evening schedule like the following:
Do PDS lab; Have snacks; If it does not rain, play soccer, otherwise if the common room is free, play table tennis, otherwise if your friend is available, play Scrabble; otherwise play guitar; Solve Maths assignment; Enjoy dinner;This is generalized in the following figure:
Repeated if-else statements
A structure of the last figure can be translated into C as:
if (Condition 1) { Block 1 } else if (Condition 2) { Block 2 } else if ... ... } else if (Condition n) { Block n } else { Block n+1 }Here is a possible implementation of the assignment y = |x|:
scanf("%d",&x); if (x == 0) y = 0; else if (x > 0) y = x; else y = -x;
Animation example : three-way branching
The switch statement
If the multi-way branching is dependent on the value of a single expression, one can use the switch statement. For example, assume that in the above figure Condition i stands for (E == vali), where E is an expression and vali are possible values of the expression for i=1,2,...,n. One can write this as:
switch (E) { case val1 : Block 1 break; case val2 : Block 2 break; ... case valn : Block n break; default: Block n+1 }Suppose you plan to write a multilingual software which prompts a thanking message based on the language. Here is an implementation:
char lang; ... switch (lang) { case 'E' : printf("Thanks\n"); break; case 'F' : printf("Merci\n"); break; case 'G' : printf("Danke\n"); break; case 'H' : printf("Shukriya\n"); break; case 'I' : printf("Grazie\n"); break; case 'J' : printf("Arigato\n"); break; case 'K' : printf("Dhanyabaadagaru\n"); break; default : printf("Thanks\n"); }The switch statement has a queer behavior that necessitates the use of the break statements. It keeps on checking if the value of the top expression matches the case values. Once a match is found, further comparisons are disabled and all following statements before the closing brace are executed one by one.
Animation example : switch
In order to avoid this difficulty, you are required to put additional break statements as and when required. This statement causes the program to leave the switch area without proceeding further down the area.
Animation example : switch with break
Interactive animation : switch with break
There are, however, situations where this odd behavior of switch can be exploited. Let us look at an artificial example. Suppose you want to compute the sum
n + (n+1) + ... + 10for n in the range 0<=n<=10. For other values of n, an error message need be printed. The following snippet does this.
sum = 0; switch (n) { case 0 : case 1 : sum += 1; case 2 : sum += 2; case 3 : sum += 3; case 4 : sum += 4; case 5 : sum += 5; case 6 : sum += 6; case 7 : sum += 7; case 8 : sum += 8; case 9 : sum += 9; case 10 : sum += 10; break; default : printf("n = %d is not in the desired range...\n", n); }