The Case for the Default Case

In one of my articles, introducing C language programming, I discussed the switch statement and I describe the default case. This week, I’ll attempt to convince you that the default case is a lot more useful than you might think.

Review Review Review

The switch statement works like a multi-way if statement. Rather than just choosing one of two paths based on a true/false condition, the switch statement chooses one of up to 1023 paths based on the value of an integer control variable.

int8_t variable;

variable = SomeFunction();
switch ( variable) {

}

The control variable is specified in the switch statement.

switch ( variable) {
  case 17:
  break;

  case 42:
  break;
}

Within the switch statement, you provide a set of cases that look like a set of labels, they are followed by colons.

switch ( variable) {
  case 1:
  case 2:
     statement;
     statement;
  break;

  case 17:
     statement;
     statement;
  break;

  case 42:
     statement;
     statement;
  break;
}
statementAfterTheSwitch;

After the case label, you put as many statements as you wish. Execution will continue until a break statement is encountered, after which execution jumps out of the switch structure to the next statement after the closing brace of the switch. Multiple case values can share a set of statements like case values 1 and 2 above.

switch ( variable) {
  case 17:
     statement1;
     statement2;

  case 42:
     statement3;
     statement4;
  break;
}

If you miss putting in a break statement, the compiler won’t complain, it trusts that you know what you are doing. Execution will simply continue with the statements in the next case until a break statement is encountered.

It is good style to match every case with a break and not let execution fall through. If a new case is inserted in the wrong spot by some future person, your code will now fall through into the wrong code block. If you feel that falling through cases is the best approach, mark it with a comment saying that you are intentionally falling through and why, your future self will appreciate it.

switch ( variable) {
  case 17:
     statement;
     statement;
  break;

  case 42:
     statement;
     statement;
  break;

  default:
     statement;
     statement;
  break;
}

C provides a special case label called default that handles all values that have not been specified in the other cases.

#define HHGTTG 42
#define CONSEQUENCES 17

int main(int argc, const char * argv[]) {
   uint32_t caseVariable;

   ...

   caseVariable= rand();
   
   switch ( caseVariable) {
       case CONSEQUENCES:
           printf("Lead me in with a count of seventeen!\n");
       break;

       case HHGTTG:
           printf("Don\'t Panic!\n");
       break;

       default:
           printf("An unexciting number, %d\n", caseVariable);
       break;
   }
   return 0;
}

The default case is used to process the cases that don’t need exceptional actions.  

That shouldn’t happen

The default case is optional and could be left out, but a better practice is to always put it in and leave a comment that it is intentionally left blank. You have the annotation that you thought about all of the cases and the default case doesn’t need to do anything.

Bugs happen, default can help you detect them. If all of your possible cases are explicitly covered and your default case is going to be left blank, you can add some code to deal with the impossible cases:

If your control variable gets accidentally overwritten by a badly handled array write or a wayward pointer somewhere else in your program, the default case can be used to pick up the illegal value.

In embedded systems, where cost is constrained, and we’re not always running with perfect power, temperature, or radiation shields, and memory can have transient errors. Your control variable might get corrupted (not likely, but it can happen), and the default case can pick off these situations and restart the system to clear it up.

Or the next time you’re keying in data into some app and it asks you for some number (like the security code on your credit card) put in the number “beer”. Users are unpredictable, and as hard as we try it’s difficult to handle every possible input, but default can pick off the stuff that you haven’t thought about.

Being Assertive

In your default case, when you’re trapping all of these weird faults, you can use the assert function to dump out a message that helps you figure out what is going on.

#include < assert.h>     /* assert */
   ...
   default:
      assert( caseVariable != caseVariable);
   break;

Assert has a parameter that has to be true or else it prints out a message stating the condition that failed as well as the filename and line number that caused the problem, then it kills your program. This code gives:

Assertion failed: (caseVariable != caseVariable), function main, file ../src/switch.c, line 39.

default:
   printf("Impossible switch value %d\n", caseVariable);
   assert( caseVariable != caseVariable);
break;

To go deeper, use printf to dump out the value of caseVariable. But remember to put the printf before the assert or the printf will never get executed.

Assert( done)

That’s it for this review of the default case, next time I’ll show you a really cool thing that GCC does with switch.


This post is part of a series. Please see the other posts here.


 Music to work by: Selected Ambient Works 85-92 by Aphex Twin