Conditionals
COP-3223H
Table of Contents
Conditionals
- Same instructions always run
- No way to vary instructinos during execution
Examples
- Guessing game
- Determine min or max values
- Check whether magic of nine succeeded (result is 9)
if statements
if (CONDITION) { // if the condition is true
INSTRUCTIONS // run these instructions
}
// either way, continue running from here
- If the condition is true during execution, run the instructions
- Otherwise, don't run the instructions.
Desktop calculator language version?
Notice the condition is inverted
if (a < b) { INSTRUCTIONS }
REST_OF_PROGRAM
becomes
if a >= b goto skiplabel INSTRUCTIONS skiplabel: REST_OF_PROGRAM
Comparison operators
| Operator | Description |
|---|---|
a == b |
Test for equal |
a != b |
Test for not equal |
a < b |
Test for less-than |
a > b |
Test for greater-than |
a <= b |
Test for less-than-or-equal |
a >= b |
Test for greater-than-or-equal |
https://www.gnu.org/software/c-intro-and-ref/manual/c-intro-and-ref.html#Numeric-Comparisons
Comparison operators have lower precedence than arithmetic, so be careful when writing them. Wrapping them in parentheses is good practice.
https://www.gnu.org/software/c-intro-and-ref/manual/c-intro-and-ref.html#Binary-Operator-Grammar
Game: Guessing game
#include <stdio.h>
int main(int argc, char **argv) {
int x;
scanf("%d", &x);
if (3 == x) {
printf("you guessed right!\n");
}
}
Under the hood
| EXPRESSION | BOOLEAN VALUE |
|---|---|
| 0 | False |
| Non-zero | True |
if (EXPRESSION) { // if EXPRESSION is non-zero, run the instructions
}
C typically uses 1 for True
| BOOLEAN VALUE | EXPRESSION VALUE |
|---|---|
| False | 0 |
| True | 1 |
This is not guaranteed and any non-zero value will work as true, i.e., run the instructions in an if statement.
Desktop calculator implementation
if (condition) {
instructions;
}
next_instructions;
// compute the conditional expression's value condition // save the result of the conditional expression store result // skip the if-statement's body if 0, i.e., false if result = 0 goto skiplabel // evaluate the body if the result is non-zoer, i.e., true instructions skiplabel: next_instructions
Guessing game: conditional value
#include <stdio.h>
int main(int argc, char **argv) {
int x;
scanf("%d", &x);
printf("x is %d\n", x);
int result;
result = (3 == x);
printf("result is %d\n", result);
if (result) {
printf("you guessed right!\n");
}
}
Comparisons are operations
3 == 3 is
3 == 3 is 1 (non-zero is true)
3 == 1
3 == 3 is 0 (zero is false)
More examples - 3 < 4 is 1 (non-zero is true) - 3 < 1 is 0 (zero is false) - 3 != 1 is 1 (non-zero true) - 3 != 3 is 0 (zero is false)
How true and false work in C
- Zero is false, don't run the conditional block
- Non-zero is true, run the conditional block
#include <stdio.h>
int main(int argc, char **argv) {
if (0) {
printf("will if (0)'s block run?\n");
}
if (1) {
printf("will if (1)'s block run?\n");
}
if (-1) {
printf("will if (-1)'s block run?\n");
}
if (74832) {
printf("will if (74832)'s block run?\n");
}
}
C gotcha: assignment = vs. equality ==
Assignments are expressions
x = y = z;
This is right-associative, i.e., right-most assignment happens first.
Result of the expression is the assigned value.
x = (y = z);
Example: assignment expressions
#include <stdio.h>
int main(int argc, char **argv) {
int x = 1;
int y = 2;
int z = 3;
x = y = z;
printf("%d\n", x);
printf("%d\n", y);
printf("%d\n", z);
}
Behavior of assignment
#include <stdio.h>
int main(int argc, char **argv) {
int x = 1;
int y = 2;
int z = 3;
x = (y = z);
printf("x is %d\n", x);
x = 1;
y = 2;
z = 3;
if (y = z) {
printf("y is %d\n", y);
}
}
y = z evaluates to 3, which gets assigned to x. Similarly, the if statement testing y = z checks whether 3 is "true", i.e., non-zero, which it is, so the if block runs.
Example: assignment in conditionals
#include <stdio.h>
int main(int argc, char **argv) {
int x;
scanf("%d", &x);
int result;
result = (x = 3);
printf("x = 3 results in %d\n", result);
if (x = 3) { // always true
printf("x = 3 is always true\n");
}
result = (x = 0);
printf("x = 0 results in %d\n", result);
if (x = 0) { // assignment
printf("x = 0 is always false\n");
}
}
Difference between equals and assignment
#include <stdio.h>
int main(int argc, char **argv) {
int x;
scanf("%d", &x);
if (x == 3) { // equality
printf("you guessed right!\n");
}
if (x = 3) { // assignment
printf("you sure you guessed right?\n");
}
}
C tip: put variable on the right-hand side
#include <stdio.h>
int main(int argc, char **argv) {
int x;
scanf("%d", &x);
if (3 == x) { // equality
printf("you guessed right!\n");
}
if (3 = x) { // assignment
printf("you sure you guessed right?\n");
}
}
What happens to this program?
Examples: comparison operators
Min and max
- Given two numbers
- Print the maximum of the two
- or either if the number is the same
#include <stdio.h>
int main(int argc, char **argv) {
int a;
int b;
printf("enter the first number: ");
scanf("%d", &a);
printf("enter the second number: ");
scanf("%d", &b);
if (a > b) {
printf("the first number, a, is greater: %d\n", a);
}
if (b > a) {
printf("the second number, b, is greater: %d\n", a);
}
}
Magic of nine: check if done
- Use an if statement to check whether we need to continue summing the digits
#include <stdio.h>
int main(int argc, char **argv) {
int num;
// read an integer from the user
scanf("%d", &num);
// print the number from the user
printf("%d\n", num);
int x;
// multiply by nine
x = num * 9;
printf("%d\n", x);
// get the ones, tens, and hundreds digits
int ones_digit = x % 10;
int tens_digit = (x / 10) % 10;
int hundreds_digit = (x / 10 / 10) % 10;
// sum the first three digits
int sum = ones_digit + tens_digit + hundreds_digit;
// print the sum
printf("%d\n", sum);
if (sum != 9) {
printf("we need to continue summing the results digits\n");
}
}
if-then-else statements
if (CONDITION) { // if the condition is true
IF_INSTRUCTIONS // run these instructions
} else {
ELSE_INSTRUCTIONS // otherwise, run these instructions
}
// either way, continue running from here
- If the condition is true during execution, run the instructions
- Otherwise, don't run the instructions.
Desugaring if-then-else with if
How can we simulate if-then-else using if statements?
else means run if the condition is not true
if (a < b) {
x = x + 1;
} else {
x = x - 1;
}
is equivalent to
if (a < b) {
x = x + 1;
}
if (a >= b) { // else is like the opposite of the if condition
x = x - 1;
}
In general:
- Invert (negate) the condition of the if branch
- Make another if statement using that opposite (inverted) condition
Meaning of else
Can both branches of an if-then-else statement be run?
No, because the semantics of an if-then-else statement are:
- Check the condition
- If it's non-zero (true), run the if branch's body, then continue running after the if-then-else statement
- If it is zero, run the else branch's body, then also continue running after the if-then-else statement
First Boolean operator: negation
!means negate the condition- Negate means swap the true and false values
Mutual exclusion of if-then-else branches
- if-branch runs when
(condition)is true - else branch runs when
(! condition)is true - Can
(condition)and(! condition)ever both be true at the same time?
Intuitively, no, because the (! condition) only runs when the condition branch does not run.
Negation truth table
| CONDITION | ! CONDITION |
|---|---|
| True | False |
| False | True |
Negation truth table (C)
| CONDITION | ! CONDITION |
|---|---|
| 1 | 0 |
| 0 | 1 |
Proof of mutual exclusion
| CONDITION | ! CONDITION | Both CONDITION and ! CONDITION true? |
|---|---|---|
| 1 | 0 | No |
| 0 | 1 | No |
We can show from the definition of negation as a truth table that the branches are mutually exclusive, because the conditions can never be true at the same time.
if-then-else as two if-statements
if (condition) {
} else {
}
is equivalent to
if (condition) {
}
if (! condition) {
}
Boolean logic
Example: bounds checking
Problem: write a program that checks whether some number x is between two numbers a and b (exclusive), where a is always less than b
- Input: an integer
- Output: "yes" when within bounds, "no" when not within bounds
Definition says a and b are not equal (a always less than b). If input number is the same as a or b, it is not within bounds, because bounds are exclusive
Do we have one comparison operation that can check this? No, but we can combine two conditions.
Boolean AND (conjunction)
&& is AND (think ampersands mean and in natural language).
if (first_condition && second_condition) {
}
These are double-ampersands && to distinguish from a single ampersand & which performs bitwise AND (i.e., perform AND on each of the corresponding bits in two binary numbers).
Semantics of AND
- Evaluate each condition
- When both are true, the result is true
- If either or both are false, the result is false.
Solution: bounds checking
How do we write the bounds condition?
a < x && x < b
#include <stdio.h>
#include <assert.h>
int main(int argc, char **argv) {
int a; // lower bound
int b; // upper bound
int x; // number to check
printf("enter the lower bound: ");
scanf("%d", &a);
printf("enter the upper bound: ");
scanf("%d", &b);
// we can quickly write assumptions to protect program from running without the assumptions we make
assert(a < b);
printf("enter the number to check: ");
scanf("%d", &x);
if (a < x && x < b) {
printf("yes\n");
} else {
printf("no\n");
}
}
Truth table for AND
first |
second |
first && second |
|---|---|---|
| 1 | 1 | 1 |
| 1 | 0 | 0 |
| 0 | 1 | 0 |
| 0 | 0 | 0 |
Bound checking improved
Problem: write a program that checks whether some number x is between a and b (exclusive), where a is always less than b where a and b are not the same
- Input: an integer
- Output: "yes" when within bounds, "no" when not within bounds
What's the condition?
- When a < b,
a < x && x < b - When b > a,
b < x && x < a
If we want to express several options among conditions, we can use OR
Boolean OR (disjunction)
|| is OR
if (first_condition || second_condition) {
}
| has also been used for OR-like operations in math, e.g., | for alternation in regular expressions
Semantics of OR
- Evaluate each condition
- If both are false, the result is false
- If either (or both) are true, the result is true
Does this pattern look familiar?
AND has the same "shape", i.e., one condition is about both between the same, and the other condition is about either (or both) being the same, except the values are the opposite.
!(a && b) is the same as !a || !b
Solution: bounds checking improved
How do we write a single bounds condition?
(a < x && x < b) || (b < x && x < a)
Notice that a < x && x < b implies that a < b.
Alternatively, we could ensure the bounds conditions only apply to their individual cases with
((a < b) && (a < x && x < b)) || ((b < a) && (b < x && x < a))
#include <stdio.h>
#include <assert.h>
int main(int argc, char **argv) {
int a; // lower bound
int b; // upper bound
int x; // number to check
printf("enter the lower bound: ");
scanf("%d", &a);
printf("enter the upper bound: ");
scanf("%d", &b);
// we can quickly write assumptions to protect program from running without the assumptions we make
assert(a != b);
printf("enter the number to check: ");
scanf("%d", &x);
if ((a < x && x < b) || (b < x && x < a)) {
printf("yes\n");
} else {
printf("no\n");
}
}
Truth table for OR
first |
second |
first || second |
|---|---|---|
| 1 | 1 | 1 |
| 1 | 0 | 1 |
| 0 | 1 | 1 |
| 0 | 0 | 0 |
Boolean operations as if statements
OR
if (! condition) {
INSTRUCTIONS;
}
if (condition) {
} else {
INSTRUCTIONS;
}
AND
Both conditions must hold.
Equivalent to using two, nested if statements
if (first_condition && second_condition) {
TRUE_INSTRUCTIONS
} else {
FALSE_INSTRUCTIONS
}
if (first_condition) {
TRUE_INSTRUCTIONS
} else {
if (second_condition) {
TRUE_INSTRUCTIONS
} else {
FALSE_INSTRUCTIONS
}
}
Or
if ( first_condition || second_condition ) {
}
Only one condition needs to hold
Equivalent to nesting the second condition in the else of the first if-then-else
if (first_condition || second_condition) {
TRUE_INSTRUCTIONS
} else {
FALSE_INSTRUCTIONS
}
if (first_condition) {
TRUE_INSTRUCTIONS
} else {
if (second_condition) {
TRUE_INSTRUCTIONS
} else {
FALSE_INSTRUCTIONS
}
}
Game: 2-dimensional collision detection
Diagram, visually show the "collision" conditions on a 2d plane with x and y coordinates
Also, work on alignment conditions, e.g., same x bounds or same y bounds
Game: the price is right (Boolean logic)
- Input the real price and two guesses
- Pick the guess that is closest without going over
Prepare all conditions
Model with Boolean logic
Design the conditional branches
- Input all three
- Check that each is greater than 0
- Check if both went over, both lost
- Check if either went over, the other won
- Use else branches
- If both under, that with the smaller distance from price is the winner
else if, (nested else branches, sequence of possibilities)
C gotchas and tips
C gotcha: conditional expression precedence
In math:
! like - && like * || like +
In C:
Warning: Never rely on the relative precedence of ‘&&’ and ‘||’. When you use them together, always use parentheses to specify explicitly how they nest, as shown here:
if ((r != 0 && x % r == 0)
||
(s != 0 && x % s == 0))
C gotcha: curly braces
The Apple goto fail vulnerability: lessons learned
the "accidental" goto fail bug NVD - CVE-2014-1266
solution: always use brackets
related: {} vs. ; confusion, if no {}, then you have ; because blocks contain statements
C gotcha: short-circuiting
int x;
x = 1;
printf("before: %d\n", x);
if (0 && (x = 3)) { // assignment of x?
}
printf("after: %d\n", x);
x = 1;
printf("before: %d\n", x);
if (1 && (x = 3)) { // assignment of x?
}
printf("after: %d\n", x);
C tip: always have an else branch
- Mistakes in Boolean logic are easy to make
- Have an else branch especially if you believe all cases have been covered already
- Put an assert statement in the else branch you think will never be reached
Think of what the else branch would mean. Some exceptions: if the condition is very simple, part of a specific pattern, just debugging info, etc.
Simple anti-example: use if statement to check for input conditions, but fail to handle what happens when the input does not meet the conditions.
Another example-example: assume the input has specific bounds, write if condition that assumes those bounds. See the bounds checking assumption. You may have a number between a and b but the condition doesn't find it because it assumes a < b. An else branch would have helped you catch this error and reason about it.