Conditionals

Introduction

The C language has conditional statements, also called selection statements (and colloquially called if statements). Essentially, depending on a certain condition, a program can decide which statements to execute and which ones to ignore.

The simplest selection statement is the if statement:

if ( expression )
  statement
Note that the parentheses after the if keyword are required.

You read this as:

"If expression is true, then execute statement."
You could also read it as:
"If expression is false, then do not execute statement." (In which case statement is simply skipped.)
Notes about expression: Relational operators: (They all have the same precedence)
< less than
> greater than
<= less than or equal to
>= greater than or equal to
Equality operators: (They both have the same precedence)
Operator Meaning
== equal to
!= not equal to
Note that the relational operators have higher precedence than the equality operators. (Operators in C)

Some example usage:

Statement Correct/Incorrect
if (a > 5)
  statement
Correct
if (a)
  statement
Correct
if (1)
  statement
Correct
if a < 5
  statement
Missing parentheses
IF (a < 5)
  statement
Wrong 'if' keyword
if (a < 5) then
  statement
No 'then' keyword
if ()
  statement
Missing expression

Note: The value of a relational expression is always either 0 (false) or 1 (true).

Examples of the relationship between false/0 and true/1: (all statements print either 0 or 1)
int a = 5;
int b = 0;

printf("Value of a > b is %i\n", a > b);    /* 1, (true)  */
printf("Value of a < b is %i\n", a < b);    /* 0, (false) */
printf("Value of a == b is %i\n", a == b);  /* 0, (false) */
printf("Value of a == a is %i\n", a == a);  /* 1, (true)  */
printf("Value of b == b is %i\n", b == b);  /* 1, (true)  */
printf("Value of a != a is %i\n", a != a);  /* 0, (false) */
printf("Value of a > a is %i\n", a > a);    /* 0, (false) */
printf("Value of b > b is %i\n", b > b);    /* 0, (false) */
Output:
Value of a > b is 1
Value of a < b is 0
Value of a == b is 0
Value of a == a is 1
Value of b == b is 1
Value of a != a is 0
Value of a > a is 0
Value of b > b is 0
In fact, some compilers will warn about these kinds of things:
a == a     a != a     a > a    etc...
with warnings like:
warning: self-comparison always evaluates to true [-Wtautological-compare]
warning: self-comparison always evaluates to false [-Wtautological-compare]
Logical operators: (the precedence is accurate as well)
Operator Meaning
! logical NOT (negation)
(unary operator)
&& logical AND
|| logical OR
Boolean Truth Tables:
a b a && b a || b
false false false false
false true false true
true false false true
true true true true
a b a && b a || b
0 0 0 0
0 1 0 1
1 0 0 1
1 1 1 1

Notes about these operators:

Note: Remember, the logical operators, || and && are different from the other operators we've seen. These operators enable short-circuit evaluation so it is possible that a portion of the expression could be skipped entirely. This means that if there are any side-effect operators in the part of the expression that is skipped, those side-effects will NOT occur.


More on the if Statement

We've seen the simplest form of the if statement:
if ( expression )
  statement
where statement is exactly one statement. If you want to execute multiple statements, you need to include curly braces around them:
if ( expression )
{
  statements
}
The statements (plural) means more than one statement. Example:
  /* single statement */
if (a > b)
  printf("a = %i, b = %i\n", a, b);

  /* compound statement */
if (a > b)
{
  printf("a = %i, ", a);
  printf("b = %i\n", b);
}
Note that there is no semicolon after the closing curly brace. (But each statement inside the braces ends with a semicolon.) This is a classic beginner's mistake (especially if you come from Python):
  /* doesn't do what you might think */
if (a > b)
  printf("a = %i, ", a);
  printf("b = %i\n", b);
And, again, today's compilers will suspect you're unsure of what you're doing:
warning: this 'if' clause does not guard... [-Wmisleading-indentation]
      if (a > b)
      ^~
note: ...this statement, but the latter is misleadingly indented as if it were guarded by the 'if'
        printf("b = %i\n", b);
        ^~~~~~
Also, it doesn't hurt to put a single statement inside curly braces:
  /* Braces unnecessary, but legal. */
if (a > b)
{
  printf("a = %i, b = %i\n", a, b);
}
This is also legal:
  /* Pointless, but legal. */
if (a > b)
{
}
However, without the braces, you can't have an empty statement. You'll need at least a semicolon:
  /* Pointless again, but legal. */
if (a > b)
    ;
Watch out for this common beginner's error which claims that 0 is greater than 5:
int a = 5;
int b = 0;

if (b > a);  /* Trailing semicolon! */
  printf("b is greater than a\n");
Fortunately, most compilers will alert you to this mistake. (You may have to enable extra warnings.):
warning: if statement has empty body [-Wempty-body]
  if (a == 4);
             ^
note: put the semicolon on a separate line to silence this warning

or 

warning: suggest braces around empty body in an 'if' statement [-Wempty-body]
Another common beginner's mistake is this:
if (a = b)  /* Assignment, not equality */
  printf("a is equal to b\n");
but, again, the compiler (Clang here) comes to the rescue:
warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
  if (a = b)
      ~~^~~

note: place parentheses around the assignment to silence this warning
  if (a = b)
        ^
      (    )
note: use '==' to turn this assignment into an equality comparison
  if (a = b)
        ^
        ==
If you add redundant parentheses, it will prevent the compiler from complaining:
if ((a = b))
  printf("a is equal to b\n");
Realize, of course, that the above is still incorrect. The compiler just doesn't warn you about it so you should only do this if it is truly what you mean.


The else Clause

Another form of the if statement includes an optional else clause:
if ( expression )
  statement1
else 
  statement2
This reads as: "If expression is true, execute statement1, otherwise, execute statement2. This is mutually exclusive. Either statement1 or statement2 will get executed, but not both (or neither).

Either of the statements (or both) can be compound as well:

if ( expression )
{
  statements
}
else 
  statement
if ( expression )
  statement
else 
{
  statements
}
if ( expression )
{
  statements1
}
else 
{
  statements2
}

Example:

int average = 85;
char grade;

if (average >= 70)
{
  grade = 'P';
  printf("You passed. Your average is %i%%.\n", average);
}
else
{
  grade = 'F';
  printf("You didn't pass. Your average is %i%%\n", average);
}


Nested if Statements

Sometimes we need to perform more than one test to determine the path our program will take. If the conditionals are mutually exclusive, we can cascade or nest the if statements.

Examples (assume average is 85):

Non-nestedNested (cascading)Nested (no formatting)
if (average >= 90)
  grade = 'A';
if (average >= 80)
  grade = 'B';
if (average >= 70)
  grade = 'C';
if (average >= 60)
  grade = 'D';
if (average < 60)
  grade = 'F';
if (average >= 90)
  grade = 'A';
else 
  if (average >= 80)
    grade = 'B';
  else 
    if (average >= 70)
      grade = 'C';
    else 
      if (average >= 60)
        grade = 'D';
      else
        grade = 'F';
if (average >= 90)
  grade = 'A';
else 
if (average >= 80)
  grade = 'B';
else 
if (average >= 70)
  grade = 'C';
else 
if (average >= 60)
  grade = 'D';
else
  grade = 'F';

Can you see why the non-nested version will possibly execute slower than the nested version (besides being incorrect)?

The proper way to format nested if statements in this class:

if (average >= 90)
  grade = 'A';
else if (average >= 80)
  grade = 'B';
else if (average >= 70)
  grade = 'C';
else if (average >= 60)
  grade = 'D';
else
  grade = 'F';
Remember that the compiler doesn't care about formatting and will actually see this, all on one line:
if (average >= 90) grade = 'A'; else if (average >= 80) grade = 'B'; else if (average >= 70) grade = 'C'; else if (average >= 60) grade = 'D'; else grade = 'F';
In fact, can you take out all of the spaces as well? If not, which ones can you take out?
Example: if(average>=90)grade='A';else if(average>=80)grade='B';else if(average>=70)grade='C';else if(average>=60)grade='D';else grade='F';


The "Dangling" else

This doesn't print out what you might expect:
if (average < 90)
  if (average < 60)
    printf("Failing\n");
else
  printf("An A student!\n");
If we change the formatting, we can see the problem more clearly.
if (average < 90)
  if (average < 60)
    printf("Failing\n");
  else
    printf("An A student!\n");
Again, compilers don't need any formatting, but humans do.
if (average < 90)
{
  if (average < 60)
    printf("Failing\n");
}
else
  printf("An A student!\n");
The rule for matching up if and else is:
The else matches the closest (previous) if that hasn't already been matched.
You override this behavior through the use of braces, as shown above.

Fortunately, again, most compilers will notice your "misleading formatting" and will say so if you do something like this:

if (average < 90)
  if (average < 60)
    printf("Failing\n");
else
  printf("An A student!\n");
Compiler warning:
warning: suggest explicit braces to avoid ambiguous 'else' [-Wdangling-else]
 if (average < 90)
    ^

The switch Statement

The switch statement is similar to nested if ... else ... statements. The most common form of the switch statement looks like this:
switch ( expression )
{
  case constant_expression1 : 
    statements1
    break;
  case constant_expression2 : 
    statements2
    break;
  . . .
  case constant_expressionN : 
    statementsN
    break;
}
An example showing both a nested if ... else ... statement and a switch statement. The result is the same. However, when you have a large number of conditions, the switch statement may execute faster.

Nested ifswitch
if (year == 1)
  printf("Freshman\n");
else if (year == 2)
  printf("Sophomore\n");
else if (year == 3)
  printf("Junior\n");
else if (year == 4)
  printf("Senior\n");
switch (year)
{
  case 1: 
    printf("Freshman\n");
    break;
  case 2: 
    printf("Sophomore\n");
    break;
  case 3: 
    printf("Junior\n");
    break;
  case 4: 
    printf("Senior\n");
    break;
}
/* break statement jumps to here */

Notice that if the value of year is not one of the values tested, nothing will be printed. If you want a catch-all condition, you would use an else clause in the if statement. For the switch statement, use a default:
Nested ifswitch
if (year == 1)
  printf("Freshman\n");
else if (year == 2)
  printf("Sophomore\n");
else if (year == 3)
  printf("Junior\n");
else if (year == 4)
  printf("Senior\n");
else
  printf("Invalid year\n");
switch (year)
{
  case 1: 
    printf("Freshman\n");
    break;
  case 2: 
    printf("Sophomore\n");
    break;
  case 3: 
    printf("Junior\n");
    break;
  case 4: 
    printf("Senior\n");
    break;
  default: 
    printf("Invalid year\n");
    break;
}
/* break statement jumps to here */

Notes:


Comparing if/else with switch

This example shows how a compiler may be able to optimize a switch statement better than if/else:

if/elseswitch
if (a == 1)
{
  /* do something */
}
else if (a == 2)
{
  /* do something */
}
else if (a == 3)
{
  /* do something */
}
else if (a == 4)
{
  /* do something */
}

/* Many, many more conditionals */

else if (a == 128)
{
  /* do something */
}
switch (a)
{
  case 1:
    /* do something */
    break;
  case 2:
    /* do something */
    break;
  case 3:
    /* do something */
    break;
  case 4:
    /* do something */
    break;



  /* Many, many more cases */

  case 128:
    /* do something */
    break;
}
For example, how many comparisons will be made if a is 128 in the above code? Suppose a is 13 using the numbers below:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16
if vs. switch - A Digipen Exclusive!

Boolean Types

As was stated before, C doesn't have a boolean type. Instead, it uses 0 to represent false and 1 (or non-zero) to represent true.

Using one and zero, the meaning isn't clear:

int value = 1;

if (value == 1)
{
  /* do something if value is true */
}

if (value == 0)
{
  /* do something if value is false */
}
We can "create" our own boolean values and type:
#define FALSE 0
#define TRUE  1
#define BOOL int
And use these types in our programs:

Explicit comparisonsImplicit comparison
BOOL value = TRUE;

if (value == TRUE)
{
  /* do something if value is true */
}

if (value == FALSE)
{
  /* do something else if value is false */
}
BOOL value = TRUE;

if (value)
{
  /* do something if value is true */
}

if (!value)
{
  /* do something else if value is false */
}

What the compiler sees after preprocessing:
Explicit comparisonsImplicit comparison
int value = 1;

if (value == 1)
{
  /* do something if value is true */
}

if (value == 0)
{
  /* do something else if value is false */
}
int value = 1;

if (value)
{
  /* do something if value is true */
}

if (!value)
{
  /* do something else if value is false */
}