#include <stdio.h>
int main () {
int n = 42;
int h = 0, v = 0;
printf ("Guess a number ? ");
scanf ("%d", &v);
if (v < 0) {
fprintf (stderr, "Only positive numbers\n");
return -1;
}
if (v == n) printf ("Good job!\n");
else {
printf ("Keep trying!");
printf ("Number is %s\n", v < n ? "HIGHER" : "LOWER");
/* On fail show a hint */
printf ("Hint : ");
h = (v > n) ? v - n : n - v;
if (h < 5) printf (" TOO HOT!!!\n");
else if (h < 10) printf (" HOT\n");
else if (h < 15) printf (" WARM\n");
else printf (" COLD\n");
}
return 0;
}In C and C++, the if-then-else construct does not
require us to write the then, which is quite common (Bash
and Lua are exceptions). As we can see in the example, when each part of
the if-else block follows a single statement, the curly
braces {} are not necessary. In fact, in C and C++ several
statements grouped with {} are syntactically a single
statement.
The condition of the if statement must always be
enclosed in parentheses and, as in most other languages, the
else block is optional. It is possible, as can be seen in
the program, to add a new if block after the
else clause to further refine the program flow.
C allows defining if-then-else blocks in an alternative
way, although beyond curiosity, its interest is limited. We will see
this construction in other languages like Python or Bash, and it allows
us to rewrite the block of code that generates the hint as follows:
(h < 5) && puts ("TOO HOT") ||
(h < 10) && puts ("HOT") ||
(h < 15) && puts ("WARM") ||
puts ("COLD");This construction takes advantage of the way the
&& and || operators are evaluated.
Since a logical AND requires both operators to be true, the second
expression (in this case, the puts statement) must be
evaluated if the first is true. If the first is false, there is no point
in evaluating the second since the result of the AND will be 0,
regardless of the value of the second expression.
These expressions can be chained with the || operator
which performs a logical OR and follows a similar logic, but taking into
account that an OR is only false if both operators are false. In the
case that the first operator is true, it is not necessary to evaluate
the second (which would be equivalent to checking the next condition),
but in case it is false, it is necessary to check the second value to
obtain the result.
The ternary operator is ?: and is used as follows:
(condition) ? then-do-this : else-do-this
DID YOU KNOW
The sequence of
ifs in our example could have been written using the ternary operator. It would be something like this:(h < 5) ? printf (" TOO HOT!!!\n") : (h < 10) ? printf (" HOT\n") : (h < 15) ? printf (" WARM\n") : printf (" COLD\n");As you can see, the structure is very similar to the use of
cond && inst1 || inst2, the difference is that the ternary operator is an operator and therefore allows us to build expressions, while the use of logical operators does not. In other words, the ternary operator allows us to return an arbitrary value, while the logical operation will only return zero or 1.
I’ll tell you in advance that languages that provide this operator do
so using this same syntax (Python is the exception). The last line where
we calculate the value of h to generate the hint, could be
rewritten as follows:
h = abs (v - n);Where abs is the function that calculates the absolute
value of an integer and is defined in stdlib.h.
NOTE
The
scanf(Scan with Format) function is the complement toprintfand allows us to read values from standard input. The string passed as the first parameter is called a format string (format string) and allows us to specify the types of the data we want to read into the variables passed as subsequent parameters.In this case
%dmeans Decimal and is used to read a number in decimal format.Other languages provide similar solutions and often use the same (or very similar) set of characters for format strings.
To finish, conditions, both the if statement and the
ternary operator ?:, do not have to be boolean operations
in C, but it is recommended that they are. In the case that they are
not, a value 0 or NULL is equivalent to the
logical value FALSE and anything else is equivalent to
TRUE.
DID YOU KNOW
The C language introduced the
booltype to represent boolean values in the C99 standard. In C++, thebooltype has been supported since always and in Objective-C, although we can useboolthe same as in C99, a large amount of code uses the type redefinitionBOOLwhich is nothing more than anunsigned char. Objective-CBOOLtakes valuesYESandNOinstead of thetrueorfalseof the standardbooltype.
C (as well as C++ and Objective C) also offers the
switch command, which can be seen as a sequence of
ifs. Its syntax is as follows:
switch (variable) {
case CONSTANT1: //code
break;
case CONSTANT2: //code
break;
default: //Code
}This code will evaluate the variable and jump to the
corresponding case or to the default label in
case the value of the variable is not listed in any of the cases.
switch works literally as we have described. The program
flow is diverted to the corresponding case and therefore we
must end the block of each case with a break statement or
otherwise the program will continue executing sequentially passing
through all the remaining cases.
I’M CURIOUS
Look up Duff’s Device (Duff’s Device) to see a curious use of
switch
C++ defines the attribute [[fallthrough]] to indicate
the situation in which the break is not included on purpose
and it is expected that the following code will be executed. For
generated code purposes the attribute has no effect and is only used by
compilers or utilities that perform static analysis of the code
reporting things like forgetting the break in a
switch.
This program uses three integer variables: v,
n and h. In C it is necessary to declare the
variables (in reality this applies to all) before being able to use
them, that is, give them a name (v, n or
h in this case) and a type that in the previous example is
the integer type int.
I’M A BEGINNER
Variables allow us to store values and modify them as needed in our programs. In this case, the variable
vis used to read the value entered by the user. The variablehis used to store an intermediate result. We could reuse the variablevsince we don’t use it again in the rest of the program, but in general, that is not a very good idea unless we want to look cool (or have serious memory restrictions), since it makes the program harder to follow.
It’s good practice to give variables names with meaning, especially nowadays that we don’t have memory restrictions and development environments will write most of the names for us. However, in simple programs or functions, it’s sometimes practical to use short names, especially if there are very long operations or conditions that become difficult to read. In the end, use common sense. In this example, the program is so simple that there’s really no difference between one approach or the other. But maybe the code would be a little easier to read declaring the variables as:
int number;
int hit, value;The variable n… well, it doesn’t change value, so it
could be a constant… In C/C++ we can do this in several ways:
#define N 42
const int n = 42;Traditionally C uses the preprocessor directive #define.
In this case, what the pre-processor does is literally replace the value
of H by 42 throughout the code before
compiling it… Just like a text processor would. In general, this works
perfectly, but it’s important to know what we’re doing to avoid unwanted
effects. #define is really a substitution macro rather than
a formal constant.
In the second case, the keyword const tells the compiler
that this variable can only be initialized once, after which its value
cannot be modified. While this is not entirely true, in programs that
don’t do tricks with pointers the declaration will function as a
constant for compilation purposes.
The main advantage of the second method is that we have type information. If our string is an integer, our constant also is. Once again, this is not a big problem if you know what you’re doing, but otherwise, C/C++’s automatic type promotions can play tricks on you.
Summary
- Supports
if ... else [if] ...and(condition) && ... || ... - Supports ternary operator
(cond) ? true_value : false_value - Versions prior to C99 do not support booleans:
==0 -> FALSE and !=0 TRUE - Supports
switch (variable) { case VALUES: break; default:} - Constants with preprocessor:
#define H 42(just a sequence of characters) - Constants with modifier:
const int h = 42(associated type)