C development on Linux – Flow Control – IV.

Introduction

You have already been exposed to a small part of what flow control is in our previous part, namely the section about relational operators. As you start writing more complex programs, you will feel the need to control the order in which your program executes various parts.
Flow control is present in most programming languages in one form or another, and what you’re about to read here is essential to writing C programs.

if/else/else if

This part of flow control is probably the most intuitive and simpler, although you can easily fall to the dark side and start writing unintelligible code with ifs. The idea is simple: if (condition_is_true) do_something; else do_something_else; . So it’s all about logic, binary logic that is, in which an expression can have two values: true or false. If you used C or Java, you’re used with the bool datatype. A bool variable can be only true or only false at a given moment. But C, although it doesn’t have the bool datatype, makes it easy to deal with binary logic, as you will see.

Let’s say you want to tell the user of your program if he’s old or not, depending on his age. Not quite useful and possibly offending, but for the sake of illustrating our point, it will do. So the main idea is: if the age entered is above a threshold, then we tell the user he’s old. If not, we tell him/her he/she is still young and blooming. The code for such a program would look like this:

#include <stdio.h>
#define LIMIT 50

int main()
{
  int age;
  printf("Hello, please enter your age!\n");
  scanf("%d", &age);
  if(age < LIMIT)
  {
    printf("Your age is %d.\n", age);
    printf("Quite young, I say.\n");
  }
  else if(age == LIMIT)
  {
    printf("You say your age is %d.\n", age);
    printf("Almost there.\n");
  }
  else
  {
    printf("So your age is %d, huh?\n", age);
    printf("Geezer.\n");
  }
  
  return 0;
}

This program is clearly of no practical use, but there are elements in it that will help us get our point across and illustrate some new elements. For example you will see we defined a constant named LIMIT (it’s recommended to capitalize your constants) with a value of 50, which is the threshold we talked about above. Next you will notice that C doesn’t use ‘then’ after the if expression like the Bourne shell does, for example. Finally, we wrote this program like this because we can illustrate another important concept: blocks. A block is a series of instructions that belong together, united by braces. Please bear in mind that if you use else if you can omit the final else, depending on the situation.

So, our first block says “if age is smaller than 50, print ‘Your age is $age’ and print ‘Quite young, I say’. When you start reading other people’s code, you will notice blocks are used a lot in C , and we recommend you use them whenever you need them and sometimes even when you don’t, to make your code more accesible to mere mortals. What was meant by “even when you don’t”? Well, C allows you to nest ifs and things may go south very easily and create bugs that will be hard to trace, or your code can become a mess to read by others and even you, so if you plan to really use nested ifs and can’t live without them, we recommend you abuse the use of braces for clarity. There are lots of situations where the logical AND operator can save you and make your code become more readable. Consider the following example:

int number = 3;

if ((number > 2) && (number < 4))
{
  printf ("number is three");

/* This could have been written like this:*/

int number =3;

if (number > 2)
{
  if (number < 4)
  {
    printf ("number is three");
  }
}

Again, this is a simple example, but I think you got the point. Use whatever method necessary and remember that ‘&&’ is not always a substitute for nested ifs, but if you need overly complicated if structures, you probably need to rethink your program’s logic.

while and for

With this section of our article, we introduce another essential concept of C programming: loops. A loop allows you to repeat a certain instruction or block depending on a condition, that is, execute something until some condition changes its truth value from true to false. As you can see, this concept is related to conditional instructions and they can be used together if need be.

while

The theoretical concept of while is ‘while (expression_is_true) execute_something;’ . With each iteration, the expression in reevaluated and if it’s still true, the instruction(s) is/are executed again, until the expression we test against becomes false. From here we can infer that if we want to write an infinite loop using while, we can write

while(1)
{
  do_stuff();
  do_more_stuff();
}

Like we said, C doesn’t have a bool keyword, but you can do something to overcome this: you can compile your programs to adhere to the C99 edition of the standard (-std=c99 as a gcc flag), which will let you access the _Bool datatype, you can use stdbool.h which defines 1 as true and 0 as false or you can define TRUE and FALSE with preprocessor instructions. What method do you think would work better and why? How would you rewrite the code snippet above considering what was said above?

Anyway, let’s continue with a complete, working example. Let’s say we want to output some message on the screen 5 times. We will talk about the same example later using for, but for now let’s see how to do it with while.

#include <stdio.h>

int main()
{
  int i;
  i = 5;
  while(i != 0)
  {
    printf("Hello!\n");
    i--;
  }

  return 0;
}

So while executes the instructions between its braces until ‘i != 0’ evaluates as false, that is when i equals zero, then it stops. For this loop to work, we need to decrement i at each pass, until it reaches zero.

Exercise

while loop interactive CNow, considering the following flow control design on your right, modify the above code to conform. Do you find these designs useful?

[TIP]: Read till the end of the article, you might find some useful hints there.

for

A loop written with for is more compact and organized, but it does the same thing as a while loop: evaluate an expression and execute something if the expression is true. This means that there are situations where the instructions may not execute at all, if the condition is false from the start. You’ll see in a whim why this is important. Using for vs while is a matter of situation, habit and personal preference, so there isn’t really anything one can do and the other can’t.

A for loop has three parts: initialization, loop, increment/decrement. It’s important to know that any part of the three can be omitted, but the semicolons, as you will see, must remain. So, a infinite loop with for would look like this:

for(;;)
{
  do_something();
  do_something_else();
}

Now, provided you have i already declared as an integer, but not defined, how would you write the code that outputs “Hello!” five times using a for loop? It’s pretty easy when you look at it carefully, so try to avoid Google or other sources of inspiration. The feeling you’ll have when you will have solved this for yourself is next to nothing.

switch/case

If you want to use an interactive program and you realize that at one point you’ll have to deal with multiple options, chosen from a list of constants, then switch is what you need. This situation is often encountered when writing interactive applications, where you will use dialogs like this: “If you want to do that, press that; if you need this, press this” and so on. For example, we will show you a program that shows you an integer value that you introduce in hex or octal, depending on your choice.

#include <stdio.h>

int main()
{
  char option;
  int number;
  
  printf("Please enter the number you want converted.\n");
  /*Please refrain from using gets() because of its
 * insecure "features" */
  scanf("%i", &number);
  
  printf("What kind of conversion do you need?\n");
  printf("Press 'o' for octal and 'x' for hexadecimal.\n");
  
  while((option = getchar()) != EOF && (option = getchar()) != '\n')
  {
    switch(option)
    {
      case 'o':
        printf("The number in octal is 0%o.\n", number);
        break;
      case 'x':
        printf("The number in hex is 0x%x.\n", number);
        break;
      default:
        printf("Option not valid.\n");
  break;
    }
  }
  return 0;
}

Switch CNow let’s dissect the program and see what and how it does things. One thing newly introduced here is the getchar() function, as defined in stdio.h . It’s used here to get a single character from user input and then write the character to a variable. We could have used option = getchar() once, before the while, but we wrote the code like this to emphasize how you can use it. We’ll leave it up to you to figure out why we check for EOF and the newline character, and we encourage you to try and see what happens if you omit those checks. The syntax of a switch statement is pretty simple and self-explanatory, so we’ll be pretty brief.

We use break; in every case because otherwise the loop would continue to the next tag (tags are what is written before the colon). The default: tag is not mandatory, but it’s useful to do something in case to other tag matches the existing data, and it’s also considered good programming practice. As another exercise, we recommend you try and rewrite our code below using scanf() instead of getchar() and see how it goes. Will it work?

do/while

We said earlier that while and for evaluate first and execute after, so there are chances the instructions might never get executed. There will be situations when you will want the exact reverse, and this where do/while enters the stage. The logical flow is inverted, as compared to while, as in do (something) while (condition_is_true). So the evaluation is done after the execution, which guarantees at least one round before the compiler realizes that the condition is false (or not).

Let’s see how an infinite loop would look like with do/while:

do
  printf("Hello!\n");
while(1);

You can simply try to verify how the flow goes by simply replacing 1 with 0 in the above code and see what happens: The program will print ‘Hello!’ once, before realizing that the while expression evaluates as false. do/while constructions are usually less used that their counterparts, but you will see that there are situations where they make your life easier. Can you give an example?

break, continue, and goto

We already “met” break before, and it can be simply described as the method of getting out of a loop in other ways than the default. You can use it with loops or switch constructions, as opposed to continue, which doesn’t really make sense in a switch. We’ll leave it to you to write some code where break and continue are used and useful, and we will continue with one of the C programmer’s “enemies”: goto. I started programming with BASIC, and I still shiver when I remember the use of goto there, and while C has it too, its use is not recommended in any case, maybe except for some system-related programs. It’s not recommended because with goto you can easily turn your work into spaghetti code, that is, code that’s very hard to read and debug because the reader is forced to “jump” to various sections of the code in order to understand it. But for the sake of completeness, here’s how it works. You declare a label, afterwards you assign some instructions to it and then you can use it in different portions of your code. Usually you can get away with a custom function instead of this, so use goto ONLY when all else fails.

if(error_unknown)
  goto error;

/*[...]*/
error:
printf("Generic error!.\n");

Now whenever you have an untreated/unknown error, you can use the error goto label to print that very helpful message. Again, avoid goto like the plague. It’s easier than you might realize to get used to it and create a bad habit of writing spaghetti code. We can’t emphasize this enough.

Conclusion

Provided you’ve read this part carefully and tried to solve the challenges we posed, you now have made another step in the land of C programming. Try to read and write as much code as possible, and don’t be afraid to ask if something goes wrong.

Here is what you can expect next:



Comments and Discussions
Linux Forum