Vinay's C Tutorial

Control Flow

This chapter depends on the knowledge of expressions. If you're not sure about them, then you should review the chapter on Expressions. As in the last chapter, this chapter does not contain many complete example programs. Instead you should write your own programs to try out what you learn.

The next chapter will deal with programming examples, where you'll learn to write all kinds of interesting programs with the basics you've learnt in the first five chapters.

Introduction

This chapter deals with another fundamental concept in almost all programming languages... control flow. In the chapter on Loops you learnt how to write programs that do more than just execute line by line, from the first line to the last. Loops enabled you to write programs that do repetitive tasks, with ease. However other things like decision making were lacking. In this chapter we'll fill that gap.

The if Statement

The if statement is used to make decisions. Its syntax is as follows

if (boolean-expression)
     statement1;
else
     statement1;

Statement1 is executed if the boolean-expression is true (i.e. non-zero), and Statement2 is executed if it is false (i.e. zero). The else part of the if statement is optional. Remember that the statements can be compound statements, enclosed in braces {}. For example,

for (i = 1; i < 5; i++)
{
     printf ("%d is", i);

     if (i % 2 == 0)
          printf ("even.");
     else
          printf ("odd.");
}

Can you guess what this program does? It prints:

1 is odd
2 is even
3 is odd
4 is even
5 is odd

Often, the statement used with else is another if so you get something like this

if (i % 2 == 0)
     printf ("%d is divisible by two", i);
else if (i % 3 == 0)
     printf ("%d is divisible by three, but not two", i);
else
     print ("%d is divisible by neither 2 nor 3", i);

A Glance At User Input

Before we can get on with more examples on the if statement, and indeed with most of the other statements in this chapter, it would be useful to briefly glance at user input. For now, we'll focus on simple character but character input. We'll of course deal with user input in more detail in later chapters.

Recall that you can have character type variables by declaring them as type char. We'll use these character variables to read from the keyboard. Take a look at the following program:

#include <stdio.h>

main()
{
     char c;

     c = getchar();

     while (c != 'a')
     {
          printf ("%c", c);
          c = getchar();
     }
}

What we've done here is to first assign c to getchar(). What does this mean? Remember, that I told you that printf was a standard function? Well, meet your second standard function! This function accepts no arguments (just like out own main() function!) that's why we've got empty brackets. However, it returns a char value. This char value happens to be a character entered at the keyboard.

If you've not understood the meaning of a function 'returning a value' let me explain that in terms of the mathematical sine function. You know that sine of 30° is 0.5. So, when one says sin(30°), sin is the function 30° is the argument, and 0.5 is the returned value. 

Given that, rest of the program's logic it pretty simple, if you've understood the preceding chapters. (I might sound boringly repetitive when it comes to emphasizing the importance of understanding the preceding chapters, but there honestly is no point in going forward without first understanding the concepts at hand!)

However, a little more insight into the working of the getchar() function is important. What it does is described as buffered input. The first time you call getchar(), it reads a full line of text (terminated by a newline character '\n' i.e. by pressing enter on the keyboard). Then it returns the first character. The next call returns the next character, and so on till the complete line is exhausted. After that, it will read the next line and so on.

So, if you run this program and enter the line "Programming". The first call to getchar() will return 'P'. Then 'P' will be written to the screen. The next call will return 'r' and so on till 'a' is encountered. So the final output of the program will be:

Progr

Now try entering the line "ProgrAmming" and press enter. The program will then dump the whole line back to the screen and wait again for you to enter another line of text. This will continue until you enter a line containing a small a. Remember that to the computer, 'a' and 'A' are completely different.

Note that the above program could also have been written as:

#include <stdio.h>  /* required for printf and getchar  */

main()
{
     char c;

     while ((c = getchar()) = 'a')

          printf ("%c", c);
}

Although this code is more compact, it is exactly equivalent to the previous program, but more compact. I'd avoided this before, to make the program easier to understand.

The switch Statement

Lets say we want to write a program that accepts a number from the user and, say prints it out in words. For simplicity, lets only consider single digit numbers. Here's one way of doing it:

#include <stdio.h>

main()
{
     char c;

     c = getchar();

     if (c = '0')
          printf ("Zero\n");
     else if (c == '1')
          printf ("One\n");
     else if (c == '2')
          printf ("Two\n");
     else if (c == '3')
          printf ("Three\n");
     else if (c == '4')
          printf ("Four\n");
     else if (c == '5')
          printf ("Five\n");
     else if (c == '6')
          printf ("Six\n");
     else if (c == '7')
          printf ("Seven\n");
     else if (c == '8')
          printf ("Eight\n");
     else if (c == '9')
          printf ("Nine\n");
     else
          printf ("That's not a valid digit!\n");
}

In this case we've only got a single statement per condition. But imagine how complicated the code would appear if we had a number of statements to be executed. For such things, C offers the switch statement.

The equivalent switch statement would be:

switch (c)
{
     case '0': printf ("Zero\n"); break;
     case '1': printf ("One\n"); break;
     case '2': printf ("Two\n"); break;
     case '3': printf ("Three\n"); break;
     case '4': printf ("Four\n"); break;
     case '5': printf ("Five\n"); break;
     case '6': printf ("Six\n"); break;
     case '7': printf ("Seven\n"); break;
     case '8': printf ("Eight\n"); break;
     case '9': printf ("Nine\n"); break;
     default: printf ("That's not a valid digit!\n");
}

A switch statement begins with switch with the variable or expression to be tested in brackets, in this case c. Now, one or more case statements are included in braces. Execution starts at the matching case statement, and continues till a break is encountered. If none of the cases match, the default statements are executed. The default statement is optional.

Note that the values in the case statements must be constant values. For example, this is illegal:

case getchar() :     /* illegal! */

It is interesting to note that if the breaks are omitted, execution will 'fall through' to the next case and so on. For example, in this program, if you omit the break statements from all cases, and then enter five. The program will them print:

Five
Six
Seven
Eight
Nine
That's not a valid digit!

If you retain the break in the case '9' the last line won't be printed. Try a few experiments with adding and removing breaks. Can you think of a situation where it will be useful?

More On Loops

You learnt about Loops in Chapter 3. At that time I'd intentionally left out one little bit because it would have been very hard to demonstrate its use without if statements. Now we're ready to deal with that little topic.

Remember, that so far, the only way to exit a loop has been for the boolean expression to become false. However, there are situations where one might want to exit the loop prematurely. In this case you use the break statement. Just like it immediately breaks you out of the switch statement, a break can be used to immediately end a loop. For example, lets rewrite the character input program. I'll only write the loop this time.

while (1)
{
     c = getchar();
     printf ("%c", c);
     if (c == '\n')
          break;
}

One modification you should immediately spot is that it checks for '\n' instead or 'a'. Recall that '\n' is a newline character... in other words, it ends a line. So what this program does is to read one line or text from the keyboard.

Another interesting thing is the while (1) statement. A boolean expression is considered true if it is non-zero. So giving 1 makes it an infinite loop (apparently). Using for (;;) has the same effect, and is often used.

The loop ends as soon as a newline character is detected, because of the break.

Another statement which can be used with loops is continue. What it does is to immediately continue with the next iteration of the loop. For example:

for (i = 1; i <= 5; i++)
{
     printf ("%d ", i);

     if (i == 3 || i == 5)
          continue;

     printf ("%d", i);
}

There is nothing different about this loop except for the if statement, which executes continue if i is 3 or 5. In other words, if i is 3 or 5, the second printf  statement won't execute. Instead it will immediately proceed with the next iteration of the loop. Of course, when i is 5, that is the last iteration of the loop, so it will exit.

What you get is this:

1 1 2 2 3 4 4 5
        ^-----^--
NOTE: 3 & 5 are not printed twice!

Nested Loops

You can put one loop inside another. This makes the inner loop execute repeatedly. This is called nesting. Remember that the variable you use to control a for loop retains its value when the loop exits... so if you don't use different variables, the outer loop will go haywire! Here are an example of nesting:

for (i = 1; i <= 5; i++)
{
     for (j = 1; j <= i; j++)
          printf ("%d", i);
     printf ("\n");
}

This program prints this pattern:

1
12
123
1234
12345

The value of i (which is modified by the outer loop) is used to determine the limits of the inner loop. Thats why it prints 1 the first time, 12 the second time and so on. The

printf ("\n");

Is not inside the inner loop... it executes after the inner loop is done, and skips to the next line. If you remove that statement you'll get:

112123123412345

You can also put different kind of loops inside another, for example, a while loop inide a for loop. Can you think of a use for that? Try experimenting with a few loops.

The goto Statement

In almost any C book you see, the goto statement is always introduced only with great reluctance, and with strict warnings against its frequent use. Although that caution is not entirely unwarranted, let's not get carried away. Anyway, let me first introduce this evil statement.

A goto statement immediately transfers control to another location in the program, located by a label. A label is defined as:

<identifier>:

i.e. a label name, followed by a colon (that's colon : not semicolon ;) However, many compilers give an error if there is no statement following the label, so if the label lies at the end of a compound statement (i.e. just before the closing brace }) you should include a semicolon after the colon.

The naming rules for labels are the same as that for variable names.

A goto statement is written as:

goto <identifier>;

For example,

for (;;)
{
     c = getchar();

     if (c == '\n')
          goto End;
     printf ("%c", c);
}
End:
.
.  /* Program continues */
.

Now all this program does is to exit the for loop as soon as a newline is encountered. In this case (but not in general) the effect is similar to that of a break statement.

Now, let's get back to why goto is such an evil statement. The reason is that it can transfer control anywhere, without regard to whether the target is inside a loop, outside a loop or some other place. This can create havoc with the program's logic and make it extremely difficult to comprehend. If possible you should use the more elegant structures like break and continue instead.

Another word of caution though... don't go overboard on this, one way or the other... Don't go out of your way to avoid gotos. There are situations where it can simplify the code. However, if the label is somewhere far away, just add a comment stating where you jump to, and if possible, one at the label, stating the place from where you can jump there. One situation where gotos are often used is to break out of multiple nested loops.

What You've Learnt

Moving Ahead

No chapter is complete till you've written a few programs on your own. Try out these programs before moving to the next chapter.

The Next Step

If you're done with this chapter and are ready to move on, click the link below!

Chapter 6: Basic Input/Output

Back to Top


I Want Your Feedback - Sign My Guestbook (View Guestbook)
If you have any suggestions, contributions or comments, E-mail me at vinaypai@crosswinds.net