To share this page click on the buttons below;
Making decisions
Every program (except our simple examples so far) needs to make some decisions and those decisions will depend on some conditions. Conditions will be evaluated to see if they are true or false so that every programming language has to define in a very neat way what exactly means true and false. In C language 0 is false, everything else is true.
Furthermore C language define a series of operators (called relational) which are able to evaluate a condition which involves 2 variables. Each of these relational operators is like a function, once evaluated (called if you want) it returns true if the condition is true and false otherwise.
Making a decision in C means executing or not some code depending on a particular condition, that condition is often expressed by a relational operator.
Relational operators
C relational operators are:
- lower than <
a < b returns true if the value of the variable a is lower than the variable b and false otherwise - grater than >
a > b returns true if the value of the variable a is greater than the variable b and false otherwise - lower or equal to ≤
a ≤ b returns true if the value of the variable a is lower or equal to the variable b and false otherwise - greater or equal to ≥
a ≥ b returns true if the value of the variable a is greater or equal to the variable b and false otherwise - equal to ==
a == b returns true if the value of the variable a is equal to the variable b and false otherwise - different from !=
a != b returns true if the value of the variable a is different to the variable b and false otherwise
If, then, else
The statement if, then, else
allows you decide which part of code execute depending on some conditions. The following pseudo-code shows this statement (pseudo-code here means that this is C code except for the fact that what is enclosed between ‘<‘ and ‘>‘ has to be considered as a placeholder for some ‘actual‘ code).
if( <condition 1> ) {
/* code for condition 1
executed only if condition 1 is true */
}
else if( <condition 2> ) {
/* code for condition 2
executed only if condition 2 is true
(and condition 1 is false) */
}
else if( <condition 3> ) {
/* code for condition 3
executed only if condition 3 is true
(and condition 1 and condition 2 are false) */
}
else {
/* code if all previous conditions are false */
}
The if, then, else statement evaluates one or more conditions (in the pseudo code abode there are 3 but we can add as many conditions as we want) in the order they are presented: so first is evaluated the condition enclosed in round parenthesis after the if
reserved word, if that condition is true then the code specified in the pair of braces placed immediately after the if
is executed, if not the second condition (which is specified in the first else if
) is evaluated, if it is true the code enclosed in braces after that else if
is executed, otherwise the evaluation of the conditions will continue. If none of the conditions in the if / else if
branches is true, then it is executed the code associated with the last else
. As soon as one condition is true the other ones are ignored and no more evaluated. All else if
conditions along with the last else
are optional (they can be present but they are not necessary).
The following is a very simple (and unfortunately rather useless) example (available here) which shows the if, then, else
statement. To make things a little bit more interesting this example will introduce some new
words to our C language vocabulary.
#include <stdio.h>
/* program to show the use of the if, then, else statement */
void main(void) {
int a = 0;
printf("Enter a number (between 0 and 4):> ");
scanf("%d",&a);
printf("You entered the number: %d\n", a);
if(a < 0) {
printf("Error: the number is lower than 0\n");
return;
}
if(a > 4) {
printf("Error: the number is higher than 4\n");
return;
}
if(a <= 1) {
printf("The number is lower or equal than 1\n");
}
else if(a >= 3) {
printf("The number is higher or equal to 3\n");
}
else {
printf("The number is 2\n");
}
if(a == 4) {
printf("The number is equal to 4\n");
}
if(a > 1) {
if(a < 3) {
printf("The number is equal to 2\n");
}
else {
printf("The number is greater than 3\n");
}
}
}
In this example there is the possibility to give an input to our program: this is done with the function scanf
. The function is part of the module stdio
, so that its declaration is already included by the <stdio.h> include preprocessing directive along with printf
.
The scanf
function works in the opposite way respect to printf
: the print function display something on the screen, the scanf
takes the input user (from keyboard) and put it into a variable. Also scanf
(like printf
) is a rather special function that can take a variable number of arguments.
In this case that function takes 2 argument, the first one is something enclosed in double quote which expresses the form of the input expected and the second one is the variable you want to put the information entered by the user. Like printf
, scanf
uses placeholders: this is the meaning of the %d
enclosed in double quote, that means that the scanf
is expecting that an integer variable (and just that) will be supplied by the user. The second argument is the variable a
preceded by a &
. That strange notation is related to the concept of pointers and allows scanf
to modify the value of the variable that are passed to that function. Remember? The variable used by a function are local to that function (when passed as argument they are just a copy) and this makes impossible for a function to modify the value of an argument. Well, it turns out that with this special notation (the trick here is done by the &
) a function can change a variable passed as argument. The reason why that is possible will be clear when we will deal with pointers. When this technique is used we said that the variable is passed by reference rather than by value.
In this example we ask the user to enter a number between 0 and 4 (line 8): with the first and the second if
we check if the user has respected our requirement. In fact if the user enters something lower than 0 the condition of the first if
is true, so an error message is printed and the function returns. That is something to be noticed: the main
function is returning. What does exactly mean? It means that our program is finished, all the following code is skipped and the control returns to the operating system.
The very same happens if the user entered something greater than 4 thanks to the second if
condition. The following code just shows some uses of the if then else statement in different forms: some conditions are checked and depending on that conditions some message is printed on the screen.
In the last part you can see that an if, then, else statement can be nested into another if, then, else statement. That is something common to every C statement: a C statement can always be used inside another one. In this case we can make more checks in cascade to further verify the value entered by the user.
Indentation
I hope you noticed that the code of the previous example is indented: that means that what is enclosed in a pair of braces (i.e. the code to be executed once of the condition evaluated by the if, then, else has been found true) is not written immediately at the beginning of the row, some blank space is left. Is this a requirement of the C language? Well, no. It is a common and rather useful practice to make the code readable. By indenting the code inside the condition you are making evident that this code belongs to that condition (i.e. it will be executed only if that condition is true). The previous code could had been written all in a single row of text: the compiler would not had been any problem to understand it, but for a human the version with the proper indentation is much more clear and understandable.
There are different theories about the proper indentation, there are indeed books and specifications that try to define which is the best coding style to make the code readable. The debate is related not only about the indentation size (how many space to add each time some code is nested into a statement), but also to the use of spaces or tabs to make the indentation and even about the position of the braces.
The position of the braces showed in the previous example (the parenthesis is opened on the same row of the if
condition and closed below at same level the if
started) is the one preferred by me (and it seems also by Kernighan & Ritchie which are the inventors of the C language, I hope this is true, I read it but I cannot be sure about that).
About indentation (this is just to give you some parameter) I like to use 3 spaces (no tabs), actually I rather discourage the use of tabs to indent code because the length of a tab depends on the setting of your editor (while spaces are always spaces). Every decent editor for coding has some customization you can adjust to have the indentation you like, some of them will also propose you some automatic indentation function which is a rather nice utility.
Software companies usually define their own code style (where to put braces, how to indent, convention for variables and functions naming: if you are going to work for some of those you will have to follow those specific guidelines).
Ternary operator
The ternary operator is an operator which has a meaning very similar to an if / else statement (i.e. without any else if additional conditions). This special operator is intended to be used (but its use is not limited to that) to initialize some variable depending upon a condition. It is sometimes useful, but bear in mind that the ternary operator is somehow less readable than an actual if / else statement: so use it sparingly.
The syntax of the ternary operator is:
( <condition> ) ? ( <if condition is true> ) : ( <if condition if false> );
condition (which is before the ?) is evaluated, if it is true the code between ? and : is evaluated, otherwise it is evaluated the one between : and ;.
A couple of examples might help to clarify:
#include <stdio.h>
/* program to show the use of ternary operator */
void main(void) {
int a = 0;
printf("Enter a number (positive or negative):> ");
scanf("%d",&a);
int b = (a < 0) ? -a : a;
printf("Value of b: %d\n",b);
/* the following is equivalent to the previous
ternary operator */
int c = 0;
if(a < 0) {
c = -a;
}
else {
c = a;
}
printf("Value of b: %d\n",b);
}
The program (available here) takes a number (positive or negative) and always make it positive.
Here you see what I think is the proper use of the ternary operator (and its equivalent form expressed with an if / else statement): the variable b
is initialized to -a
if a
is negative or to a
otherwise. The very same is done from line 18 with the variable c
.
As you can see the ternary operator gives you a nice and short way to initialize the variable b
. Probably the ternary operator is here the best solution (I am always thinking about readability). But the ternary operator can be easily used to make the code unreadable. For example:
#include <stdio.h>
/* program to show the use of ternary operator */
void main(void) {
int a = 0;
printf("Program to perform a division\n");
printf("Enter the first number:> ");
scanf("%d",&a);
int c = 0;
printf("Enter the second number:> ");
scanf("%d",&c);
(c != 0) ? printf("The result is %d\n", a / c) : printf("Error division by 0!\n");
}
The previous code (available here) is perfectly legal (try to execute it) but now it is a little bit more difficult to understand it at a glance. The ternary operator here is used to check the value of c
. Since the program performs the division between a
and c
the value of c
has to be different to 0 (remember to always check that when you want to execute a division): if the value is actually different from 0 the division is executed (directly inside the printf
function!) and printed, otherwise an error message is printed.
There is an important thing to be noticed: the ternary operator returns the value of the expression of the condition executed. In the first example the value is a
and so that value is assigned to variable b
. In the second example the returned values is the return value of the function printf
, but this value is not used because the ternary operator is not assigned to any variable.
Switch, case
There is another useful statement used to make decisions in C: the switch, case statement. It is used when a the content of a variable has to be compared with many different values.
Using our pseudo-C the switch, case is something like that:
switch(<variable>) {
case <value 1>:
/* code to be executed if variable is equal to value 1 */
break;
case <value 2>:
/* code to be executed if variable is equal to value 1 */
break;
case <value 3>:
/* code to be executed if variable is equal to value 1 */
break;
/* .. OTHER cases ... */
default:
/* code to be executed if variable not equal to any of the
previous values */
break;
}
The content of the variable is compared with the values indicated in each case
and related code is executed. If the variable is not equal to any of the values it is executed the code contained into the default
case. You can use as many cases you need.
There is, in the pseudo-C, a little intruder which is the reserved word break
. The break
is a C word that might be used also in other contexts but it has always the same meaning: it breaks the execution of the statement is inserted in, preventing the execution of other code that can be present in the statement itself.
In the case of the switch, case statement it is used to control which code has to be executed: the switch, case in fact implements what is called the falling through behavior. That means that as soon as a value match the content of of the variable all the subsequent cases are executed, unless there is the break
statement that stops the execution.
As usual a little example (available here) might help to clarify:
#include <stdio.h>
/* program to show the use of switch / case */
void main(void) {
int a = 0;
printf("Enter an integer number between 0 and 10:> ");
scanf("%d",&a);
/* switch case WITH break that stops the falling through
behaviour */
printf("\nWith break\n");
switch(a) {
case 0:
printf("The entered value is 0\n");
break;
case 1:
printf("The entered value is 1\n");
break;
case 2:
printf("The entered value is 2\n");
break;
case 3:
printf("The entered value is 3\n");
break;
case 4:
printf("The entered value is 4\n");
break;
case 5:
printf("The entered value is 5\n");
break;
case 6:
printf("The entered value is 6\n");
break;
case 7:
printf("The entered value is 7\n");
break;
case 8:
printf("The entered value is 8\n");
break;
case 9:
printf("The entered value is 9\n");
break;
case 10:
printf("The entered value is 10\n");
break;
default:
printf("Error in entered value\n");
break;
}
/* switch case WITHOUT break that stops the falling through
behaviour */
printf("\nWithot break\n");
switch(a) {
case 0:
printf("The entered value is 0\n");
case 1:
printf("The entered value is 1\n");
case 2:
printf("The entered value is 2\n");
case 3:
printf("The entered value is 3\n");
case 4:
printf("The entered value is 4\n");
case 5:
printf("The entered value is 5\n");
case 6:
printf("The entered value is 6\n");
case 7:
printf("The entered value is 7\n");
case 8:
printf("The entered value is 8\n");
case 9:
printf("The entered value is 9\n");
case 10:
printf("The entered value is 10\n");
default:
printf("Error in entered value\n");
}
printf("\nEnd of the program\n");
}
The example shows the same switch, case in two different flavors: the first one with the break
at the end of each case
and the second one without the break
.
Now if you play a little bit entering different values you can see the effect of the falling through behavior. The version without break
is clearly not what we want here, but there are situations in which this behavior might be handy.
This ambiguity of the switch, case causes sometimes errors and unexpected situations. There are programming languages (like Python) that completely avoid the implementation of the switch, case just to avoid this ambiguity. There are programming practices that force the programmer to always put a break
for each case
. That is my suggestion: always use the break version, this may cause you to write some more code sometimes, but your code will be more readable and easy to understand and modify.
Just last note about the default
case: its presence is optional, anyway I suggest to always define that particular condition (perhaps you will leave it void, but just the fact that you write it might help you to remind that you are considering all possible situations).
To share this page click on the buttons below;