|
CS256 - Principles of Structured Design
Fall 2021
| Reading:
Comparison (Relational) operations:
Comparing two values is a common task for computers. Comparison operators are
Boolean operations, i.e. an operation that can be only true or false
and nothing else. if statements and loops use Boolean expressions to determine
if they should either do something or continue looping.
In C, any value that is non-zero is considered true, and any zero value is
considered false. Comparison and Logical operators always result in a true
or false result. Both side of a comparison should be numeric in nature (that
does include single quoted characters, since they are just numeric ASCII values.)
Comparison operator |
true if: |
Example |
< |
Less than |
a < b |
<= |
Less than or equal |
a <= b |
> |
Greater than |
a > b (true if a is greater than b) |
>= |
Greater than or equal |
a >= b (true if a is greater than or equal to b) |
== |
Equal to |
a == b (true if a and b are the same) |
!= |
Not equal to |
a != b (true if a and b are not the same) |
Examples:
ch == 'A' // True if the character ch is the letter 'A'
a <= 0 // True if a is less than or equal to 0.
0 >= a // Same as the previous.
ch != ' ' // The character ch is not a space character.
Logical (Boolean) operations:
The Boolean operators allow us to combine true/false comparison together into
more complex expressions.
Logical operator |
true if: |
Example |
&& |
Boolean AND |
a && b (true if both a & b are true) |
|| |
Boolean OR |
a || b (true if either a or b are true) |
! |
Boolean NOT |
! a (true if a is false, false if a is true) |
Boolean logic table:
a |
b |
NOT a |
a AND b |
a OR b |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
where 1 = true, and 0 = false.
Examples:
ch >= 'A' && ch <= 'Z' // True if ch is an upper-case letter (in the range A-Z)
ch == ' ' || ch == '\t' // True if the character is a space or tab.
! (ch >= 32 && ch <= 127) // True if ch is NOT a printable character (NOT
// in the ASCII range 32-127)</div>
-5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
┌─┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬
x<=10 │◀─────────────────────────────────────────────╼
x>= 0 │ ╾────────────────────────────────────────────▶
x<=10 && x>=0 │ ╾─────────────────────────────╼
!(x<=10 && x>=0)│◀────────────╼ ╾───────────▶
x<=10 || x>=0 │◀────────────────────────────────────────────────────────────▶
// The lines above represent when the Boolean expression on the left is true,
// it is false everywhere else.
Notice in the above example how when the x<=10 and x>=0 comparisons are
combined with the AND operator (&& ) that the only time the entire expression
is true is when they are both true, which is represented by when the number
lines for the individual x<=10 and x>=0 lines would overlap each other
(i.e. when both are true.)
The Compound / Block Statement:
Most of the control structures in C take a single statement, such as the if
statement we talk about that follows:
if ( expression ) statement
A single statement is rather limiting if we need to do more than one thing if
the expression is true. To combine multiple statements together into a single
logical statement we can use the compound statement or sometimes referred to
as the block statement. A compound statement is simply a sequence of
statements enclosed by curly braces ({ ...} ). Everything between the curly
braces is considered a single logical statement. Wherever we see in the
grammar a statement we can replace with a compound statement.
One thing to keep in mind with respect to variables created inside of a compound
statement is that variables declared inside of them only exist inside the
compound statement (i.e. they are block local and are not accessible outside of
the block.)
// The following is a block statement:
{
int a = foo(); // the variable a only exists inside of these {...}'s
a = a * 2;
printf("a = %d\n", a);
}
Note: that compound statements are not terminated by a semicolon
(; ), like most other statements, variable declarations or control statements.
The If Statement:
Sometimes we want to only execute some code given some conditions. We can do
this using a conditional statement. The most common form of this is the if
statement:
if ( expression ) statement
This will only execute statement if the expression inside of the () 's
evaluates to true (any non-zero value in C.) statement can be a compound
statement (statements enclosed in {} 's, like:
if ( expression ) {
statement1;
statement2;
etc;
}
Examples:
if (ch >= 'a' && ch <= 'z') printf("The character is a lowercase letter.\n");
if (ch >= '0' && ch <= '9') printf("The character is a digit character.\n");
if (n >= 0 && n <= 10 && n != 3)
printf("n is in the range 0-10, but is not 3.\n");
The If-else Statement:
Sometimes we want to two (or more) different code paths depending on the result
of the initial expression. We can do this with an else part to the if:
if ( expression ) statement1;
else statement2;
This will execute statement1 if expression is true, or it will execute
statement2 if expression is false. Either or both of statement1 or 2 can
be compound statements:
Examples:
if (ch == ' ' || ch == '\t') {
printf("The character is a space or a tab.\n");
} else {
printf("The character is not a space or a tab.\n");
}
NOTE: The else portion of an if-else statement has no associated
expression. It is just the path the computer takes if the if expression is
false.
Prematurely terminating a statement:
A common mistake beginning programmers make is to accidentally insert a
semicolon after the expression portion of if-statements or loop statements, such
as:
if (ch == ' ' || ch == '\t'); {
└────── //This terminates the if statement. *Don't do this.*
...
}
for(i=0; i < 10; i++); {
└────── //This terminates the for loop. *Don't do this.*
...
}
Though it appears grammatically correct, and the compiler will accept it, these
statements will not function the way one would expect. The lone semi-colon by
itself is the null statement, i.e. a statement that does nothing. Sometimes
this is actually what we would want (particularly with for loops,) but most of
the time it's a mistake. The block statement that follows the if or loop is a
valid block statement and will be executed once, but is not actually part of the
if or loop statement, it merely follows the previous statement, even though its
opening { is on the same line, that doesn't actually attach it to the if or
loop, that is because in C, none of the white-space (spaces, tabs and newlines)
do not actually matter to the compiler.
If-Else chaining and Nested If Statements:
Nested if statements are if statements that are inside of other if statements.
The result it very much like combining the expressions of the two if statements
together using the Boolean AND operator (&& ). For example:
if (a >= 10) {
if (a <= 20) {
// Inside of here is the same as saying (a >= 10 && a <= 20)
printf("a in the range 10 to 20\n");
} else {
printf("a is greater than 20\n");
}
} else {
printf("a is less than 10\n");
}
Although as you can see, combining it with the else clause gives us a bit
more flexibility than we would have with a single if-statement.
Some common tasks, such as converting a score to a grade, can be accomplished
using a sequence of if-statements chained off of each else statement:
// The first if-statement that is true is the only one that is taken.
if (score >= 90) printf("A");
else if (score >= 80) printf("B");
else if (score >= 70) printf("C");
else if (score >= 60) printf("D");
else printf("F");
// Structurally this is the same as this, using compound statements:
if (score >= 90) {
printf("A");
} else {
if (score >= 80) {
printf("B");
} else {
if (score >= 70) {
printf("C");
} else {
if (score >= 60) {
printf("D");
} else {
printf("F");
}
}
}
}
In the above case, you can see that each subsequent if-statement is enclosed in
the parent if-statements `else` clause.
Example codes:
min/max:
The following code examples show how to make functions to compute the largest
of 2 (max2) or 3 (max3) values:
int max2(int a, int b) {
if (a > b) return a; // If a is larger than b, return it
return b; // otherwise return b
}
int max3(int a, int b, int c) {
if (a > b && a > c) return a; // Choose a if it's larger than b or c
// At this point a cannot be the largest, so it is eliminated
if (b > c) return b; // Choose b if it's larger than c
// At this point both a and b are eliminated, so c must be the largest:
return c;
}
//Note: There is no need for else in these examples, given that return immediately
//exits the function giving it its value.
//Using max2 or max3 above might look something like:
printf("%d\n", max2(5,10)); // Will print 10
printf("%d\n", max3(5,20,3)); // Will print 20
//Try placing the numbers into the variables to the parameters to the functions
//and tracing through the code to see how it works.
The max2 function can be used by other functions to create max3, max4 and
beyond:
int max3(int a, int b, int c) {
return max2(a, max2(b, c));
}
These work like "playoffs":
a ───┐
b ─┐ ├── ?
├─┘
c ─┘
Each ├ is like the max2 function being used.
int max4(int a, int b, int c, int d) {
return max2(max2(a, b), max2(c, d));
}
a ─┐
├─┐
b ─┘ │
├── ?
c ─┐ │
├─┘
d ─┘
|