6. Control Flow#

There are three things you will learn in this chapter. Switching the path of execution in program depending upon program variables or states using control statements. Repeating a set of instructions using loops. Bypassing certain set of instructions in a loop and jump around. Collectively, these elements of C allow or enable you to take driver’s seat over the control over a C program. You will spend much of your programming time even in future using these basic elements. Let us begin with if-else without spending much time over boring stuff. Before we proceed I would like to tell you about storage classes of array and their scope. I could have covered it in second chapter but I did not want to scare you with too many things in itself.

6.1. Storage Classes#

In C99 variables can be declared at any point in class without breaking an expression. This was not possible in older C and you could only declare at startof function. There is auto, register, static and extern.

auto is the default storage class for local variables. Local variables are those that are inside any function or any control-flow statement block. You will perhaps never use auto explicitly. For examle, auto int i; register is the storage class which tell the compiler that this variable will be stored in a CPU register instead of RAM. It is mostly used for loop counters and to store small 32-bit or 64-bit variables in CPU register. For example, register int i;

static is the default storage class for global variables though local variables can also be made static. local static variables persist across function calls that is their values do not change.

extern keyword allows global variables to become visible in other modules than itself.

There are two more modifiers: const and volatile. As their names specify const makes a variable constant. That is you cannot change value of a variable declared as const. volatile is kind of opposite but not really opposite. What it does is that the programs other than the program itself where it has been declared are allowed to change it. This means that a variable can be a const as well as volatile at the same time.

6.2. if-else Statement#

An if-else statement may consist of only if or both if and else or if and else if or if, else if and else. An if-else statement must have if at the beginning, one or more else if must come after if or before else and else must come at end. else if and else are optional and may not come. Consider the following program:

//Author: Shiv S. Dayal
//Description : Demo of if-else statements.

#include <stdio.h>

int main()
{
  int i = 0, j= 0;

  printf("Please enter two integers i and j:\n");
  scanf("%d%d", &i , &j);

  if(i==4)
    printf("you entered 4 for i.\n");

  if(i==7)
  {
    printf("you entered 7 for i.\n");
    printf("I am happy for you.\n");
  }
  else
  {
    printf("You did not enter 7 for i.\n");
  }
  if(i==7)
  {
    printf("you entered 7 for i.\n");
    printf("I am happy for you.\n");
  }
  else if(j==8)
    printf("You entered 8 for i.\n");

  if(i==7)
    printf("you entered my lucky number.\n");
  else if((i==7) &&(j==8))
    printf("May god bless you!\n");
  else
    printf("You entered bad number.\n");

  return 0;
}

and the output is:

Please enter two integers i and j:
4
6
you entered 4 for i.
You did not enter 7 for i.
You entered bad number.

As you can see from first if sttatement that if you enter the value of i as 4 then the printf will be executed and you will be able to see it. Note that if there are multiple lines below if which you want to execute then you must put them in a block using curly braces. If you just want to execute one line then these curly braces are optional. Note that how you must use curly braces if you have more than one line and you want to execute them. Also, see the syntax for missing else and missing else if. One if-else can be nested inside another for example see the following code:

//Author: Shiv S. Dayal
//Description : Demo of if-else statements.

#include <stdio.h>
#include <string.h>

int main()
{
  char fName[128]={0}, lName[128]={0};

  printf("Enter your first name and last name in that order:\n");
  gets(fName);
  gets(lName);

  if(strcmp(fName, "Shiv") == 0)
  {
    if(strcmp(lName, "Dayal") == 0)
      printf("Your name is Shiv Dayal.\n");
  }
  else
  {
    printf("Your name is %s %s.\n", fName, lName);
  }

  return 0;
}

and the output is:

Enter your first name and last name in that order:
Shiv
Dayal
Your name is Shiv Dayal.

another run:

Enter your first name and last name in that order:
Richard
Stallman
Your name is Richard Stallman.

when first if matches but else does not:

Enter your first name and last name in that order:
Shiv
Stallman

Note the usage of nested if-else. Also, note how strcmp has been used to compare two strings and gets to read the input. gets is dangerous but it is simple that is why has been used here. You can read about it at the link of opengroup. We will see this in more detail towards the end when we deal with chapter named C Standard Library.

WARNING: Always remember the expression inside if evaluates to a boolean so you should never do an ASSIGNMENT inside if and else if as it will always evaluate to what is assigned. It can render all your logic meaningless. C is not Python, where assignment inside if is not allowed. However, if you assign 0 to some variable it will evaluate to false

6.2.1. Dangling Else Problem#

The else part has a property that it will cling to closest if. So the following piece of code may give you surprise:

if(x==1)
  if(y>2)
    printf("foo\n");
else
  printf("bar\n");

Now consider x!=1 then you may think that bar will be printed. However, that will not be the case. The else part clings to inner if. This can be fixed by using curly braces.

6.3. switch Statement#

switch statement is kind of if-else replacement to simplify it. Usage of switch statement is to compare one expression with others, and then execute a series of sub-statements inside case and default based on the result of the comparisons. Note that switch statement takes only integers or integreal type as its argument and same is valid for its cases. Consider the following example:

//Author: Shiv S. Dayal
//Description : Demo of if-else statements.

#include <stdio.h>

int main()
{
  int i  = 65;

  switch(i)
  {
    case 'A':
      printf("Value of i is 'A'.\n");
      break;
    case 'B':
      printf("Value of i is 'B'.\n");
      break;
    default:
      break;
  }

  return 0;
}

and the output is:

Value of i is 'A'.

Notice the usage of break. It is used to terminate execution once a match has been found for a particular case else what will happen is shown below:

//Author: Shiv S. Dayal
//Description : Demo of switch statement.

#include <stdio.h>

int main()
{
  int i  = 65;

  switch(i)
  {
    case 'A':
      printf("Value of i is 'A'.\n");
    case 'B':
      printf("Value of i is 'B'.\n");
    default:
      printf("Value of i is %c.\n", i);
      break;
    }

  return 0;
}

and the output is:

Value of i is 'A'.
Value of i is 'B'.
Value of i is A.

This is also known as fall through of a switch statement. Notice, the use of default that how it is analogous to else statement. switch statements can also be nested inside each other. However, node that lots of nesting is not good. At most 2-3 levels are more than enough else you should look at alternative ways of writing code.

6.4. while Loop#

Of three loops I am first going to cover while loop. It is simplest of three. I will just give an example for you to understand.

//Author: Shiv S. Dayal
//Description : Demo of while statement.

#include <stdio.h>

int main()
{
  int i = 0;

  while(i<=10)
  {
    printf("%d * %2d = %4d\n", 2, i, 2*i);
    i++;
  }

  return 0;
}

and the output is:

2 *  0 =    0
2 *  1 =    2
2 *  2 =    4
2 *  3 =    6
2 *  4 =    8
2 *  5 =   10
2 *  6 =   12
2 *  7 =   14
2 *  8 =   16
2 *  9 =   18
2 * 10 =   20

while loop just has one expression which is its terminating condition. We have written i<=10 which is terminating condition for our loop. The moment i will become greater than that the loop will terminated. We are initializing our loop index to 0 and incrementing within while loop. Note that you must use curly braces for body of block of loop. If you have only one statement as body of loop then braces are optional.

6.5. do while Loop#

It is very much similar to while loop but with a very subtle difference. Consider the following code:

//Author: Shiv S. Dayal
//Description : Demo of do while statement.

#include <stdio.h>

int main()
{
  int i = 0;

  do {
    printf("I am Shiv.\n");
    i++;
  }while(i<5);

  return 0;
}

and the output is:

I am Shiv.
I am Shiv.
I am Shiv.
I am Shiv.
I am Shiv.

Notice the semicolon at the end of while. Now time for that subtle difference:

//Author: Shiv S. Dayal
//Description : Demo of do while statement.

#include <stdio.h>

int main()
{
  int i = 10;

  do {
    printf("2 * 10 = 20\n");
    i++;
  }while(i<5);

  return 0;
}

and the output is:

2 * 10 = 20

Notice how do while loop executes once even if the loop index is more than the terminating condition in the while part.

6.6. for Loop#

for loop is the last of loops and most versatile. It has three parts: initialization of loop counters, terminating condition, and loop index modification. If you declare a variable in the initialization part then that variable has just loop scope while for while and do while loop indices have at least outer block scope. This makes for loop better. Consider the following example for computing squares of numbers:

//Author: Shiv S. Dayal
//Description : Demo of for statement.

#include <stdio.h>

int main()
{
  for(int i=1, j=1; i<=10||j<=10; i++, j++)
    printf("%2d * %2d = %4d\n", i, j, i*j);

  return 0;
}

and the output is:

1 *  1 =    1
2 *  2 =    4
3 *  3 =    9
4 *  4 =   16
5 *  5 =   25
6 *  6 =   36
7 *  7 =   49
8 *  8 =   64
9 *  9 =   81
10 * 10 =  100

Notice how various things are coming in picture here: initialization, terminating conditions loop counter incrementation and output formatting. Here is how you can write an infinite for loop for(;;). You can write an infinite loop anywhere if your loop index counters are not getting incremented/decremented properly or your termination condition is incorrect. Also, always make sure that loop indices are initialized. As an exercise you can try to implement this program using while and do while loop. Last line of the above output is not having first space properly.

6.7. break and continue Statements#

break statement breaks out of innermost for, do, while and switch statements. It terminates that loop. Consider for example:

//Author: Shiv S. Dayal
//Description : Demo of break statement.

#include <stdio.h>

int main()
{

  for(int i = 0;;i +=10)
  {
    if(i>100)
      break;
    printf("%d\n", i);
  }

  return 0;
}

and the output is:

0
10
20
30
40
50
60
70
80
90
100

Notice how the for loop is terminated once i goes beyond 100 even though there is no terminating condition. Try the same in while and do while loop and produce the same result.

continue statement is slightly different than break in the sense that it does not stop the execution of that loop but simply does not execute remaining instructions of that block. Consider for example:

//Author: Shiv S. Dayal
//Description : Demo of continue statement.

#include <stdio.h>

int main()
{

  for(int i = 0;i<=100;i +=10)
  {
    if(i==50)
      continue;
    printf("%d\n", i);
  }

  return 0;
}

and the output is:

0
10
20
30
40
60
70
80
90
100

Notice how 50 is missing from output.

6.8. typedef and return Statements#

typedef statement is used to define new types from existing types. For example:

typedef char s8;
typedef unsigned char s8;
typedef short int s16;
typedef unsigned short int u16;

You will be seeing its usage in function pointers, structures and unions heavily.

return statement is used to return from function. Optionally you can return a value.