Logo  

CS351 - Computer Organization

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 ─┘