This section discusses various ways of controlling program flow in C, using branching.
The goto Statement
Yes, it's our old favourite, the goto statement. Here's an example:
that_label_over_there: /* a label (surprised?) */ printf ("This always gets printed.\n"); }
To jump to another place in a C program, you have to define a label at that point. A label is any valid C identifier ending in a colon (:). In our case, the label is eloquently named ‘that_label_over_there’.
Labels are local to the function they are defined in. You can't jump from one function to another using goto. You can goto into and out of loops and other constructs. Bear in mind, however, that using goto to jump into a loop is very bad C and bound to create problems.
Generally, try not to overuse this statement. Anything you can do with a goto, you can do with the other C constructs. Do not ignore it, though! It may not be the best in structured programming, but when you're struggling to save just a byte more RAM, anything is acceptable.
Returning from a Function
The return keyword does two things: it exits the current function (this is why it's listed among the other branching statements), and returns an evaluated expression to the caller (if the function allows that, of course) 1. returning from the main function effectively exits the program. Here's an example:
void main() { printf ("foo(10)=%d\n", foo (10));
return; /* exit from the program */
printf ("Never printed either.\n"); }
The if...else Statement
This statement is the equivalent of the IF...THEN...ELSE statement in BASIC, only more powerful. Here's its syntax:
or
The if statement first evaluates expression. If it is non-zero (i.e. true), statement1 is executed. Otherwise, if the optional else part is there, statement2 is executed.
The parentheses around expression are not part of the expression; they're part of the statement and are obligatory. statement1 and statement2 can be single statements, or blocks (lots of C code enclosed in curly brackets {...} — a block is equivalent to a single statement).
Since they can be any statement, they can also be other if statements. This creates a complication: you can have nested ifs. In this case, the problem occurs when we try to decide which if an else ‘sticks’ to. The answer is simple: an else will always pair with the closest if. You can avoid this (admittedly embarrassing) situation by putting the nested ifs inside curly brackets. Let's see a couple of examples:
if (game_over) { if (rudeness > 100) blow_raspberry(); else be_nice(); } else go_on_with_game();
The first example is wrong in the sense that it doesn't do what we want to 2. The else sticks to the closest if, which is the one on the line above (the indentation of the else is wrong, it should be under the if above it). If you try to express this in words, you'll get the same kind of ambiguity. So, it's better to use blocks to disambiguate nested ifs, as in the second example, which is much clearer.
Of course, if statement2 is another if statement, you get something like the following:
or
This allows you to check for various conditions at once. The final else part is optional. Here is an example. See if you can figure it out.
if (c == 'A') move_up(); else if (c == 'Z') move_down(); else if (c == 'L') { /* using a block and a nested if */ if (can_move_right()) move_right(); else beep(); } else if (c == 'K') /* a nested if without a block */ if (can_move_left()) move_left(); /* we can't have an else here -- it would stick to the top-level if. To use an else we have to also use a block. */ else wrong_key(); }
The switch Statement
The above example is a bit awkward. switch provides a way to test an expression for a number of values, and act differently, depending on the value. Its syntax is as follows:
default: statementsN1; }
switch evaluates the expression within the parentheses (which are mandatory, like in if). Then it goes through the case statements, and compares the result of the expression to the constant next to each case. If they are equal, all statements between case and break are executed. If no match is found, and the optional default: part exists, the statements between default: and the closing curly bracket are executed. This allows for a default case, which applies if nothing else does.
Strangely enough, break is also optional! If a break keyword is not found in a case, the statements for the next case are executed as well, until a break is found, or we reach the closing curly bracket at the end of the case statement. Be careful of this, it's a nice feature in some cases 3, but try not to forget the breaks when you need them! Here's an example:
- 1. You can't return anything bigger than 32 bits (4 bytes) on the 32-bit compiler, or 16 bits (2 bytes) on the 16-bit compiler. So this limits things to
chars,ints, and other short data types. Soon we'll see how to return bigger ones (yes, the solution involves pointers). - 2. Rather, it does what we tell it to.
- 3. Pun intended.




Add new comment