Consider the following switch
statement:
switch( value )
{
case 1:
return 1;
default:
value++;
// fall-through
case 2:
ret
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;
}
yes, this is valid, and under some circumstances it is even useful. Generally, if you don't need it, don't do it.
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];
}
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.
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);
}
}
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
}