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:

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 operatorUsageCondition is true iff
==E1 == E2E1 and E2 evaluate to the same value
!=E1 != E2E1 and E2 evaluate to different values
<E1 < E2E1 evaluates to a value smaller than E2
<=E1 <= E2E1 evaluates to a value smaller than or equal to E2
>E1 > E2E1 evaluates to a value larger than E2
>=E1 >= E2E1 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                   True

The 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 operatorSyntaxTrue if and only if
ANDC1 && C2Both C1 and C2 are true
ORC1 || C2Either C1 or C2 or both are true
NOT!CC 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')        True

Notice 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)TypeAssociativity
!UnaryRight
<  <=  >  >=BinaryLeft
==  !=BinaryLeft
&&BinaryLeft
||BinaryLeft

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:

1-way branching

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:

2-way branching

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:

multi-way branching

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) + ... + 10

for 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);
   }


Course home