Switch statement: must default be the last case?

CSwitch Statement

C Problem Overview


Consider the following switch statement:

switch( value )
{
  case 1:
    return 1;
  default:
    value++;
    // fall-through
  case 2:
    return value * 2;
}

This code compiles, but is it valid (= defined behavior) for C90/C99? I have never seen code where the default case is not the last case.

EDIT:
As Jon Cage and KillianDS write: this is really ugly and confusing code and I am well aware of it. I am just interested in the general syntax (is it defined?) and the expected output.

C Solutions


Solution 1 - C

The case statements and the default statement can occur in any order in the switch statement. The default clause is an optional clause that is matched if none of the constants in the case statements can be matched.

Good Example :-

switch(5) {
  case 1:
    echo "1";
    break;
  case 2:
  default:
    echo "2, default";
    break;
  case 3;
    echo "3";
    break;
}


Outputs '2,default'

very useful if you want your cases to be presented in a logical order in the code (as in, not saying case 1, case 3, case 2/default) and your cases are very long so you do not want to repeat the entire case code at the bottom for the default

Solution 2 - C

The C99 standard is not explicit about this, but taking all facts together, it is perfectly valid.

A case and default label are equivalent to a goto label. See 6.8.1 Labeled statements. Especially interesting is 6.8.1.4, which enables the already mentioned Duff's Device:

> Any statement may be preceded by a > prefix that declares an identifier as > a label name. Labels in themselves do > not alter the flow of control, which > continues unimpeded across them.

Edit: The code within a switch is nothing special; it is a normal block of code as in an if-statement, with additional jump labels. This explains the fall-through behaviour and why break is necessary.

6.8.4.2.7 even gives an example:

switch (expr) 
{ 
    int i = 4; 
    f(i); 
case 0: 
    i=17; 
    /*falls through into default code */ 
default: 
    printf("%d\n", i); 
} 

> In the artificial program fragment the > object whose identifier is i exists > with automatic storage duration > (within the block) but is never > initialized, and thus if the > controlling expression has a nonzero > value, the call to the printf function > will access an indeterminate value. > Similarly, the call to the function f > cannot be reached.

The case constants must be unique within a switch statement:

> 6.8.4.2.3 The expression of each case label shall be an integer constant > expression and no two of the case > constant expressions in the same > switch statement shall have the same > value after conversion. There may be > at most one default label in a switch > statement.

All cases are evaluated, then it jumps to the default label, if given:

> 6.8.4.2.5 The integer promotions are performed on the controlling > expression. The constant expression in > each case label is converted to the > promoted type of the controlling > expression. If a converted value > matches that of the promoted > controlling expression, control jumps > to the statement following the matched > case label. Otherwise, if there is a > default label, control jumps to the > labeled statement. If no converted > case constant expression matches and > there is no default label, no part of > the switch body is executed.

Solution 3 - C

It's valid and very useful in some cases.

Consider the following code:

switch(poll(fds, 1, 1000000)){
   default:
    // here goes the normal case : some events occured
   break;
   case 0:
    // here goes the timeout case
   break;
   case -1:
     // some error occurred, you have to check errno
}

The point is that the above code is more readable and efficient than cascaded if. You could put default at the end, but it is pointless as it will focus your attention on error cases instead of normal cases (which here is the default case).

Actually, it's not such a good example, in poll you know how many events may occur at most. My real point is that there are cases with a defined set of input values where there are 'exceptions' and normal cases. If it's better to put exceptions or normal cases at front is a matter of choice.

In software field I think of another very usual case: recursions with some terminal values. If you can express it using a switch, default will be the usual value that contains recursive call and distinguished elements (individual cases) the terminal values. There is usually no need to focus on terminal values.

Another reason is that the order of the cases may change the compiled code behavior, and that matters for performances. Most compilers will generate compiled assembly code in the same order as the code appears in the switch. That makes the first case very different from the others: all cases except the first one will involve a jump and that will empty processor pipelines. You may understand it like branch predictor defaulting to running the first appearing case in the switch. If a case if much more common that the others then you have very good reasons to put it as the first case.

Reading comments it's the specific reason why the original poster asked that question after reading Intel compiler Branch Loop reorganisation about code optimisation.

Then it will become some arbitration between code readability and code performance. Probably better to put a comment to explain to future reader why a case appears first.

Solution 4 - C

yes, this is valid, and under some circumstances it is even useful. Generally, if you don't need it, don't do it.

Solution 5 - C

There's no defined order in a switch statement. You may look at the cases as something like a named label, like a goto label. Contrary to what people seem to think here, in the case of value 2 the default label is not jumped to. To illustrate with a classical example, here is Duff's device, which is the poster child of the extremes of switch/case in C.

send(to, from, count)
register short *to, *from;
register count;
{
  register n=(count+7)/8;
  switch(count%8){
    case 0:	do{ *to = *from++;
    case 7:	    *to = *from++;
    case 6:	    *to = *from++;
    case 5:	    *to = *from++;
    case 4:	    *to = *from++;
    case 3:	    *to = *from++;
    case 2:	    *to = *from++;
    case 1:	    *to = *from++;
            }while(--n>0);
  }
}

Solution 6 - C

One scenario where I would consider it appropriate to have the default case located somewhere else than at the end of a switch statement is in a state machine where an invalid state should reset the machine and proceed as though it was the initial state. For example:

switch(widget_state)
{
  default:  /* Fell off the rails--reset and continue */
    widget_state = WIDGET_START;
    /* Fall through */
  case WIDGET_START:
    ...
    break;
  case WIDGET_WHATEVER:
    ...
    break;
}

An alternative arrangement, if an invalid state should not reset the machine but should be readily identifiable as an invalid state:

switch(widget_state)
{
  case WIDGET_IDLE:
    widget_ready = 0;
    widget_hardware_off();
    break;
  case WIDGET_START:
    ...
    break;
  case WIDGET_WHATEVER:
    ...
    break;
  default:
    widget_state = WIDGET_INVALID_STATE;
    /* Fall through */
  case WIDGET_INVALID_STATE:
    widget_ready = 0;
    widget_hardware_off();
    ... do whatever else is necessary to establish a "safe" condition
}

Code elsewhere may then check for widget_state == WIDGET_INVALID_STATE and provide whatever error-reporting or state-reset behavior seems appropriate. For example, the status-bar code could show an error icon, and the "start widget" menu option which is disabled in most non-idle states could be enabled for WIDGET_INVALID_STATE as well as WIDGET_IDLE.

Solution 7 - C

Chiming in with another example: This can be useful if "default" is an unexpected case, and you want to log the error but also do something sensible. Example from some of my own code:

  switch (style)
  {
  default:
    MSPUB_DEBUG_MSG(("Couldn't match dash style, using solid line.\n"));
  case SOLID:
    return Dash(0, RECT_DOT);
  case DASH_SYS:
  {
    Dash ret(shapeLineWidth, dotStyle);
    ret.m_dots.push_back(Dot(1, 3 * shapeLineWidth));
    return ret;
  }
  // more cases follow
  }

Solution 8 - C

There are cases when you are converting ENUM to a string or converting string to enum in case where you are writing/reading to/from a file.

You sometimes need to make one of the values default to cover errors made by manually editing files.

switch(textureMode)
{
case ModeTiled:
default:
    // write to a file "tiled"
    break;

case ModeStretched:
    // write to a file "stretched"
    break;
}

Solution 9 - C

The default condition can be anyplace within the switch that a case clause can exist. It is not required to be the last clause. I have seen code that put the default as the first clause. The case 2: gets executed normally, even though the default clause is above it.

As a test, I put the sample code in a function, called test(int value){} and ran:

  printf("0=%d\n", test(0));
  printf("1=%d\n", test(1));
  printf("2=%d\n", test(2));
  printf("3=%d\n", test(3));
  printf("4=%d\n", test(4));

The output is:

0=2
1=1
2=4
3=8
4=10

Solution 10 - C

It's valid, but rather nasty. I would suggest it's generally bad to allow fall-throughs as it can lead to some very messy spaghetti code.

It's almost certainly better to break these cases up into several switch statements or smaller functions.

[edit] @Tristopia: Your example:

Example from UCS-2 to UTF-8 conversion 

r is the destination array, 
wc is the input wchar_t  

switch(utf8_length) 
{ 
	/* Note: code falls through cases! */ 
	case 3: r[2] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x800; 
	case 2: r[1] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x0c0; 
	case 1: r[0] = wc;
}

would be clearer as to it's intention (I think) if it were written like this:

if( utf8_length >= 1 )
{
	r[0] = wc;
	
	if( utf8_length >= 2 )
	{
		r[1] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x0c0; 

		if( utf8_length == 3 )
		{
			r[2] = 0x80 | (wc & 0x3f); wc >>= 6; wc |= 0x800; 
		}
	}
}	

[edit2] @Tristopia: Your second example is probably the cleanest example of a good use for follow-through:

for(i=0; s[i]; i++)
{
	switch(s[i])
	{
	case '"': 
	case '\'': 
	case '\\': 
		d[dlen++] = '\\'; 
		/* fall through */ 
	default: 
		d[dlen++] = s[i]; 
	} 
}

..but personally I would split the comment recognition into it's own function:

bool isComment(char charInQuestion)
{	
	bool charIsComment = false;
	switch(charInQuestion)
	{
	case '"': 
	case '\'': 
	case '\\': 
		charIsComment = true; 
	default: 
		charIsComment = false; 
	} 
	return charIsComment;
}

for(i=0; s[i]; i++)
{
	if( isComment(s[i]) )
	{
		d[dlen++] = '\\'; 
	}
	d[dlen++] = s[i]; 
}

Solution 11 - C

I had an interesting case where putting the default at the top saved programme space. It was for an Arduino Nano, and saved 8 bytes of flash (RAM was same). FYI the two sets of code are

#if 1 // toggle this 0 or 1
// 3138/265 bytes
uint8_t g_BuiltinLedGlowState = 0; // dropping '= 0' saves nothing
void AdvanceBuiltinLedGlow_3Ph(){
  switch(++g_BuiltinLedGlowState){
  default: 
    g_BuiltinLedGlowState = 0;
    // drop through // break;
  case 0: // bright
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);
    break;
  case 1: // dim
    pinMode(LED_BUILTIN, INPUT_PULLUP);
    break;
  case 2: // off
    pinMode(LED_BUILTIN, INPUT);
    break;
  }
}
#elif 1
// 3146/265 bytes
uint8_t g_BuiltinLedGlowState = 0; // dropping '= 0' saves nothing
void AdvanceBuiltinLedGlow_3Ph(){
  switch(++g_BuiltinLedGlowState){
  case 1: // bright
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH);
    break;
  case 2: // dim
    pinMode(LED_BUILTIN, INPUT_PULLUP);
    break;
  case 3: // off
    pinMode(LED_BUILTIN, INPUT);
    // drop through // break;
  default: 
    g_BuiltinLedGlowState = 0;
    break;
  }
}
#endif

// the loop function runs over and over again forever
void loop() {
  Serial.println(g_BuiltinLedGlowState, DEC);
  AdvanceBuiltinLedGlow_3Ph();
  delay(1000);
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontanasciusView Question on Stackoverflow
Solution 1 - CSalilView Answer on Stackoverflow
Solution 2 - CSecureView Answer on Stackoverflow
Solution 3 - CkrissView Answer on Stackoverflow
Solution 4 - CJens GustedtView Answer on Stackoverflow
Solution 5 - CPatrick SchlüterView Answer on Stackoverflow
Solution 6 - CsupercatView Answer on Stackoverflow
Solution 7 - CBrennan VincentView Answer on Stackoverflow
Solution 8 - CPredrag ManojlovicView Answer on Stackoverflow
Solution 9 - CScott ThomsonView Answer on Stackoverflow
Solution 10 - CJon CageView Answer on Stackoverflow
Solution 11 - CbrewmanzView Answer on Stackoverflow