Control flow. Branching. if and switch. Enumeration types

Control flow. Branching. if and switch. Enumeration types.

An exercise: Solving quadratic equations with integer coefficients

The program has to compute the roots of the quadratic equation. It should correctly identify the cases when

and report the answer accordingly.

#include <iostream>
#include <cmath>

int main() {

  std::cout 
    << "Solving an equation of the form ax^2 + bx + c = 0:" 
    << std::endl;

  int a = 5;
  int b = 6;
  int c = 1;

  std::cout 
    << "a = " << a << ", "
    << "b = " << b << ", "
    << "c = " << c << std::endl;

  int d = b*b - 4*a*c;

  if (d > 0) {
    double x1 = (-b + sqrt(d))/(2.0*a);
    double x2 = (-b - sqrt(d))/(2.0*a);
    std::cout << "Two roots:" << std::endl;
    std::cout << "x1 = " << x1 << std::endl;
    std::cout << "x2 = " << x2 << std::endl;
  }
  else if (d == 0) {
    std::cout << "One root of multiplicity two: " << std::endl;
    std::cout << "x = " << (-b/(2.0*a)) << std::endl; 
  }
  else {
    std::cout << "No roots" << std::endl;
  }

  return 0;
}
Solving an equation of the form ax^2 + bx + c = 0:
a = 5, b = 6, c = 1
Two roots:
x1 = -0.2
x2 = -1

Boolean expressions

#include <iostream>
using namespace std;

int main() {
  bool b1 = true;
  bool b2 = false;

  // Boolean operators
  bool b3 = b1 && b2;  // and   (bool, bool -> bool)
  bool b4 = b1 || b2;  // or    (bool, bool -> bool)
  bool b5 = !b1;       // not   (bool -> bool)

  int i = 2;
  int j = 3;

  // Relational operators 
  // (for numerical types, such as int and double)
  bool b6 = i <= j; // less or equal     (int, int -> bool)
  bool b7 = i >= j; // greater or equal  (int, int -> bool)
  bool b8 = i < j;  // less              (int, int -> bool)
  bool b9 = i > j;  // greater           (int, int -> bool)

  // Equality operators
  bool b10 = i == j; // equal            (int, int -> bool)
  bool b11 = i != j; // not equal        (int, int -> bool)
  // The equality operators == and != work not only for 
  // numerical types, but for comparing booleans as well

  // complex boolean expression
  bool c1 = (b1 || b5) && (!b2);
  
  // mixing arithmetic and boolean operators
  bool c2 = 
    (((0 < i) && (i < 15)) || !(-4 >= j*5 + i)) && (i != j);

  return 0;
}

“Short-circuit” (minimal) evaluation

Observe that the division by zero (5/0 == 1) is not evaluated in both if statements:

#include <iostream>
using namespace std;

int main(){
 
  if ( false && (5/0 == 1) && (0 <= 1) ) {
    cout << "first" << endl;
  }
 
  if ( true || (5/0 == 1) || (0 <= 1) ) {
    cout << "second" << endl;
  }

  return 0;
}
second

If the division by zero were evaluated, you would get a runtime error:

#include <iostream>
using namespace std;

int main(){
 
  if ( (5/0 == 1) && (0 <= 1) ) {
    cout << "first" << endl;
  }
 
  return 0;
}
Floating point exception (core dumped)

Precedence of operators

(Inclomplete) operator precedence order:

  1. Function calls (such as sqrt(2.0) or abs(-5))
  2. Unary operators +, -, and !
  3. Binary arithmetic operators
    1. * / %
    2. + -
  4. Comparisons
    1. < > <= >=
    2. == !=
  5. Binary boolean operators
    1. &&
    2. ||

This order actually does make sense! After evaluating the unary operators (there is no ambiguity at this stage), the arithmetic operators compute the numbers. Then, the comparison operators compare the numbers you’ve got to produce boolean values. Then, the logical connectives (and and or) combine those boolean values.

For example, consider the following two assignment statements:

  int b1 = 2 + 3 - 4 <= 1 || x == 4 && y / 2 != 0;
  int b2 = (((2 + 3) - 4) <= 1) || ((x == 4) && ((y / 2) != 0));

Both statements compute the same expression, however, the first relies on the operators precedence, and the second is fully parenthesized.

Avoid excessive parentheses, where the meaning and the order is clear, however, it’s good to keep some of the parentheses to make the code more readable and reduce the chance to make a mistake:

  int b3 = (2 + 3 - 4 <= 1) || ((x == 4) && (y / 2 != 0));

See pp. 51 - 52 for the full operator precedence table.

Using = in place of ==

#include <iostream>
using namespace std;
int main(){
  int x = 0;

  cout << "x = " << x << endl;

  // Instead of comparing x to 1, by mistake, we are
  // assigning x = 1 (here, x actually becomes equal to 1) 
  if (x = 1) {
    cout << "x is equal to 1!" << endl;
  }

  // Correct way to check equality:
  if (x == 2) {
    cout << "x is equal to 2!" << endl;
  }

  // A trick to avoid such errors:
  // The compiler will complain if you try to assign 3 = x
  if (3 == x) {
    cout << "x is equal to 3!" << endl;
  }

  return 0;
}
x = 0
x is equal to 1!

Automatic type conversion between int and bool

Beware! Integers and bools can be used interchangeably! If that happens, the following type conversions are automatically executed:

true is converted to 1.
false is converted to 0.
0 is converted to false.
any non-zero integer is converted to true.

#include <iostream>
using namespace std;
int main() {

  cout << true + 5 << endl;
  cout << false + 5 << endl;

  if (7) {
    cout << "7 means true!" << endl;
  }

  if (0) {
    cout << "0 is false! This will not be printed" << endl;
  }

  return 0;
}
6
5
7 means true!

switch statement

#include <iostream>
using namespace std;
int main() {
  
  int i = 4;

  if (0 == i) { cout << "zero"; }
  else if (1 == i) { cout << "one"; }
  else if (2 == i) { cout << "two"; }
  else if (3 == i) { cout << "tree"; }
  else if (4 == i) { cout << "four"; }
  else { cout << "other"; }

  cout << endl;

  switch (i) {
    case 0 : 
      cout << "zero"; 
      break;
    case 1 : 
      cout << "one"; 
      break;
    case 2 : 
      cout << "two"; 
      break;
    case 3 : 
      cout << "three"; 
      break;
    case 4 : 
      cout << "four"; 
      break;
    default:
      cout << "other";
  }

  cout << endl;

  return 0;
}
four
four

The “fall-through” behavior when break is missing:

#include <iostream>
using namespace std;
int main() {
  char c = ' ';
  cout << "Enter a character: ";
  cin >> c;

  bool paren = false;
  bool bracket = false;
  bool plus = false;

  switch(c){
    case '(':
    case ')':
      paren = true;
      break;
    case '[':
    case ']':
      bracket = true;
      break;
    case '+':
      plus = true;
      break;
  }

  if (paren) cout << "parenthesis";
  if (bracket) cout << "bracket";
  if (plus) cout << "plus";
  cout << endl;

  return 0;
}
Enter a character: (
parenthesis

Enumeration (enum) types

#include <iostream>
using namespace std;

enum Token {PAREN, BRACKET, PLUS};

int main() {
  char c = ' ';
  cout << "Enter a character: ";
  cin >> c;

  Token t = PAREN;

  switch(c){
    case '(':
    case ')':
      t = PAREN;
      break;
    case '[':
    case ']':
      t = BRACKET;
      break;
    case '+':
      t = PLUS;
      break;
  }

  switch(t) {
    case PAREN: 
      cout << "parenthesis";
      break;
    case BRACKET: 
      cout << "bracket";
      break;
    case PLUS: 
      cout << "plus";
      break;
  }

  cout << endl;

  return 0;
}
Enter a character: ]
bracket