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:
Note that the parentheses after the if keyword are required.if ( expression ) statement
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:
0 0F 0.0 NULL '\0'
a > b a a > 2 2 < a b < 2 a - b 2 a * 5 * b + 4
b > a b a < 2 2 > a a - 5 0 a * 5 * b
Equality operators: (They both have the same precedence)
< less than > greater than <= less than or equal to >= greater than or equal to
Note that the relational operators have higher precedence than the equality operators. (Operators in C)
Operator Meaning == equal to != not equal to
Some example usage:
Statement Correct/Incorrect if (a > 5)
statementCorrect if (a)
statementCorrect if (1)
statementCorrect if a < 5
statementMissing parentheses IF (a < 5)
statementWrong 'if' keyword if (a < 5) then
statementNo 'then' keyword if ()
statementMissing expression
Examples of the relationship between false/0 and true/1: (all statements print either 0 or 1)Note: The value of a relational expression is always either 0 (false) or 1 (true).
Output: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) */
In fact, some compilers will warn about these kinds of things: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
with warnings like:a == a a != a a > a etc...
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)
Boolean Truth Tables:
Operator Meaning ! logical NOT (negation)
(unary operator)&& logical AND || logical OR
Notes about these operators:
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
For example, what is the output of this program? (Hint: Put parentheses around the sub-expressions first.)
#include <stdio.h> int main(void) { int a; int b; a = 5; b = 3; if (a > b && b > 0 && ++a == 6) printf("1. The value of a is %i\n", a); a = 5; if (a > b && b > 5 && ++a == 6) printf("2. The value of a is %i\n", a); a = 5; if (a > b || b > 5 || ++a == 6) printf("3. The value of a is %i\n", a); a = 5; if (a > b && b > 5 || ++a == 6) printf("4. The value of a is %i\n", a); a = 5; if (a > b || b > 5 && ++a == 6) printf("5. The value of a is %i\n", a); return 0; }
Here is a video that explains what's happening.
In fact, the GNU compiler will actually warn you about the lack of parentheses to get you to make your intentions clearer. (When mixing || and &&)if ( ( (a > b) && (b > 0) ) || (++a == 6) ) printf("1. The value of a is %i\n", a);
This is the warning from #4 above:
warning: suggest parentheses around '&&' within '||' [-Wparentheses]
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:where statement is exactly one statement. If you want to execute multiple statements, you need to include curly braces around them:if ( expression ) statement
The statements (plural) means more than one statement. Example:if ( expression ) { statements }
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):/* 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); }
And, again, today's compilers will suspect you're unsure of what you're doing:/* doesn't do what you might think */ if (a > b) printf("a = %i, ", a); printf("b = %i\n", b);
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:
This is also legal:/* Braces unnecessary, but legal. */ if (a > b) { printf("a = %i, b = %i\n", a, b); }
However, without the braces, you can't have an empty statement. You'll need at least a semicolon:/* Pointless, but legal. */ if (a > b) { }
Watch out for this common beginner's error which claims that 0 is greater than 5:/* Pointless again, but legal. */ if (a > b) ;
Fortunately, most compilers will alert you to this mistake. (You may have to enable extra warnings.):int a = 5; int b = 0; if (b > a); /* Trailing semicolon! */ printf("b is greater than a\n");
Another common beginner's mistake is this: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]
but, again, the compiler (Clang here) comes to the rescue:if (a = b) /* Assignment, not equality */ printf("a is equal to b\n");
If you add redundant parentheses, it will prevent the compiler from complaining: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) ^ ==
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.if ((a = b)) printf("a is equal to b\n");
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).if ( expression ) statement1 else statement2
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); }
Examples (assume average is 85):
Non-nested | Nested (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:
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?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';
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';
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:if (average < 90) { if (average < 60) printf("Failing\n"); } else printf("An A student!\n");
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:
Compiler warning:if (average < 90) if (average < 60) printf("Failing\n"); else printf("An A student!\n");
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: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.switch ( expression ) { case constant_expression1 : statements1 break; case constant_expression2 : statements2 break; . . . case constant_expressionN : statementsN break; }
Nested if | switch |
---|---|
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 if | switch |
---|---|
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:
if | switch |
---|---|
if (a == b) printf("a is equal to b\n"); else if (a == c) printf("a is equal to c\n"); else if (a == d) printf("a is equal to d\n"); |
switch (a) { case b: /* ILLEGAL */ printf("a is equal to b\n"); break; case c: /* ILLEGAL */ printf("a is equal to c\n"); break; case d: /* ILLEGAL */ printf("a is equal to d\n"); break; } |
error: case label does not reduce to an integer constant case b: ^~~~ error: case label does not reduce to an integer constant case c: ^~~~ error: case label does not reduce to an integer constant case d: ^~~~
if | switch |
---|---|
if ( (year == 1) || (year == 2) ) printf("Lower division\n"); else if ( (year == 3) || (year == 4) ) printf("Upper division\n"); else printf("Invalid year\n"); |
switch (year) { case 1: case 2: printf("Lower division\n"); break; case 3: case 4: printf("Upper division\n"); break; default: printf("Invalid year\n"); break; } /* break statement jumps to here */ |
Warnings | Warnings suppressed |
---|---|
switch (year) { case 1: fresh++; case 2: lower++; printf("Another lower class\n"); break; case 3: junior++; case 4: upper++; printf("Another upper class\n"); break; default: printf("Invalid year\n"); break; } |
switch (year) { case 1: fresh++; /* FALL THRU */ case 2: lower++; printf("Another lower class\n"); break; case 3: junior++; /* FALL THRU */ case 4: upper++; printf("Another upper class\n"); break; default: printf("Invalid year\n"); break; } |
switch2.c:74:12: warning: this statement may fall through [-Wimplicit-fallthrough=] fresh++; ~~~~~^~ switch2.c:75:5: note: here case 2: ^~~~ switch2.c:80:13: warning: this statement may fall through [-Wimplicit-fallthrough=] junior++; ~~~~~~^~ switch2.c:81:5: note: here case 4: ^~~~
fall through fallthrough FALL THROUGH FALLTHROUGH fall thru fallthru FALL THRU FALLTHRU
Comparing if/else with switch
This example shows how a compiler may be able to optimize a switch statement better than if/else:
For example, how many comparisons will be made if a is 128 in the above code? Suppose a is 13 using the numbers below:
if/else switch 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; }
if vs. switch - A Digipen Exclusive!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
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:
We can "create" our own boolean values and type:int value = 1; if (value == 1) { /* do something if value is true */ } if (value == 0) { /* do something if value is false */ }
And use these types in our programs:#define FALSE 0 #define TRUE 1 #define BOOL int
What the compiler sees after preprocessing:
Explicit comparisons Implicit 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 */ }
Explicit comparisons Implicit 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 */ }