4.8 switch语句
C的switch语句颇不寻常。它类似于其他语言的case语句,但在一个方面存在重要的区别。首先让我们来看看它的语法,其中expression的结果必须是整型值。
switch( expression )
statement
尽管在switch语句体内只使用一条单一的语句也是合法的,但这样做毫无意义。实际使用中的switch语句一般如下所示:
switch( expression ){
statement-list
}
贯穿于语句列表之间的是一个或多个case标签,形式如下:
case constant-expression:
每个case标签必须具有一个唯一的值。常量表达式(constant-expression)是指编译期间进行求值的表达式,它不能是任何变量。这里的不同寻常之处是case标签并不把语句列表划分为几个部分,它们只是确定语句列表的进入点。
switch语句的执行过程。首先是计算expression的值;然后,执行流转到语句列表中其case标签值与expression值相匹配的语句。从这条语句起,直到语句列表的结束(也就是switch语句的底部),它们之间所有的语句均被执行。
警告:
switch语句的执行过程的不同之处。执行流将贯穿各个case标签,而不是停留在单个case标签,这也是为什么case标签只是确定语句列表的进入点而不是划分它们的原因。如果你觉得这个行为不是那么正确,有一种方法可以纠正---就是break语句。
4.8.1 switch中的break语句
如果在switch语句的执行中遇到了break语句,执行流就是立即跳到语句列表的末尾。在C语言所有的switch语句中,几乎在每个case中都有一条break语句。下面的例子程序检查用户输入的字符,并调用该字符选定的函数,从而说明了break语句的这种用途。
switch( command ){
case 'A':
add_entry();
break;
case 'D':
delete_entry();
break;
case 'P':
print_entry();
break;
case 'E':
edit_entry();
break;
}
#include
#include
void add_entry( void );
void delete_entry( void );
void print_entry( void );
void edit_entry( void );
int main( void ){
char command = 'A';
switch( command ){
case 'A':
add_entry();
break;
case 'D':
delete_entry();
break;
case 'P':
print_entry();
break;
case 'E':
edit_entry();
break;
default:
printf( "it's not a right command.n" );
break;
}
return EXIT_SUCCESS;
}
void add_entry( void ){
printf( "add_entry function carrys out.n" );
}
void delete_entry( void ){
printf( "delete_entry function carrys out.n" );
}
void print_entry( void ){
printf( "print_entry function carrys out.n" );
}
void edit_entry( void ){
printf( "eidt_entry function carrys out.n" );
}
break语句的实际效果是把语句列表划分为不同的部分。这样,switch就可以按照更为传统的方式工作。
在最后一个case语句后面加上一条break语句在运行时没有什么效果,因为它后面不再有任何语句,不过这样做也没有什么害处。之所以要加上这条break语句,是为了以后维护方便。如果以后有人决定在这个switch语句中再添加一个case,可以避免出现“在以前的最后一个case语句后面忘记添加break语句”这个情况。
在switch语句中,continue语句没有任何效果。只有当switch语句位于某个循环内部时,才可以把continue语句放到switch语句内。在这种情况下,与其说continue语句作用于switch语句,还不如说它作用于循环。
#include
#include
int main( void ){
int grades[] = { 44, 77, 88, 99, 55 };
int i;
int len;
char level[5] = { '0' };
len = sizeof(grades) / sizeof(*grades);
printf( "print elements of grades:n" );
for( i = 0; i < len; ++i )
{
printf( "grades[%d] = %dn", i, grades[i] );
}
printf( "n" );
printf( "print passing grades and level of grades:n" );
for( i = 0; i < len; ++i ){
switch( grades[i] / 10 ){
case 10:
case 9:
level[i] = 'A';
break;
case 8:
level[i] = 'B';
break;
case 7:
level[i] = 'C';
break;
case 6:
level[i] = 'D';
break;
default:
continue;
level[i] = 'E';
break;
}
printf( "grades[%d] = %d, level[%d] = %cn", i, grades[i], i, level[i] );
}
return EXIT_SUCCESS;
}
为了使同一组语句在有两个或更多个不同的表达式值时都能够执行,可以使它与多个case标签对应,如下所示:
switch( expression ){
case 1:
case 2:
case 3:
statement-list
break;
case 4:
case 5:
statement-list
break;
}
这个技巧能够达到目的,因为执行流将贯穿这些并列的case标签。C没有任何简便的方法指定某个范围的值,所示该范围内的每个值都必须以单独的case标签给出。如果这个范围非常大,可能应该改用一系列嵌套的if语句。
#include
#include
int main( void ){
int grades[] = { 44, 77, 88, 99, 55 };
int i;
int len;
char level[5] = { '0' };
len = sizeof(grades) / sizeof(*grades);
printf( "print elements of grades:n" );
for( i = 0; i < len; ++i )
{
printf( "grades[%d] = %dn", i, grades[i] );
}
printf( "n" );
printf( "print passing grades and level of grades:n" );
for( i = 0; i < len; ++i ){
switch( grades[i] / 10 ){
case 10:
case 9:
level[i] = 'A';
break;
case 8:
level[i] = 'B';
break;
case 7:
level[i] = 'C';
break;
case 6:
level[i] = 'D';
break;
case 5:
case 4:
case 3:
case 2:
case 1:
case 0:
level[i] = 'E';
break;
default:
printf( "the range of %d is a mistake.n", grades[i] );
break;
}
printf( "grades[%d] = %d, level[%d] = %cn", i, grades[i], i, level[i] );
}
return EXIT_SUCCESS;
}
#include
#include
#include
int main( void ){
int grades[] = { 44, 77, 88, 99, 55 };
int i;
int len;
char level[5][4] = { ' ' };
len = sizeof(grades) / sizeof(*grades);
printf( "print elements of grades:n" );
for( i = 0; i < len; ++i )
{
printf( "grades[%d] = %dn", i, grades[i] );
}
printf( "n" );
printf( "print passing grades and level of grades:n" );
for( i = 0; i < len; ++i ){
if( grades[i] > 100 ){
printf( "grades is illegal.n" );
break;
}
if( grades[i] == 100 ) {
strcpy( level[i], "A++" );
} else if( grades[i] >= 95 ){
strcpy( level[i], "A+" );
} else if( grades[i] >= 90 ){
strcpy( level[i], "A" );
} else if( grades[i] >= 85 ){
strcpy( level[i], "B+" );
} else if( grades[i] >= 80 ){
strcpy( level[i], "B" );
} else if( grades[i] >= 75 ){
strcpy( level[i], "C+" );
} else if( grades[i] >= 70 ){
strcpy( level[i], "C" );
} else if( grades[i] >= 65 ){
strcpy( level[i], "B+" );
} else if( grades[i] >= 60 ){
strcpy( level[i], "B" );
} else{
strcpy( level[i], "E" );
}
printf( "grades[%d] = %d, level[%d] = %sn", i, grades[i], i, level[i] );
}
return EXIT_SUCCESS;
}
4.8.2 default子句
如果表达式的值与所有的case标签的值都不匹配该怎么办?其实也没有什么---所有的语句都被跳过而已。程序并不会终止,也不会提示任何错误,因为这种情况在C语言中并不认为是个错误。
但是,如果并不想忽略不匹配所有的case标签的表达式值,可以在语句列表中增加一条default子句,把下面这个标签
default:
写在任何一个case标签可以出现的位置。当switch表达式的值并不匹配所有case标签的值时,这个default子句后面的语句就会执行。所以,每个switch语句中只能出现一条default子句。但是,它可以出现在语句列表的任何位置,而且语句流可以像贯穿一个case标签一样贯穿default子句。
提示:
在每个switch语句中都放上一条default子句是个好习惯,因为这样做可以检测到任何非法值。否则,程序将如无其事地继续执行,并不提示任何错误出现。这个规则唯一合理的例外是表达式的值在先前已经进行过有效性检查,并且你只对表达式可能出现的部分值感兴趣时。
4.8.3 switch语句的执行过程
计算程序输入中字符、单词和行的个数。每个字符都必须计数,但空格和制表符同时也作为单词的终止符使用。换行符也是行的终止符,同时也是单词的终止符。例如:
switch( ch ){
case 'n':
lines += 1;
case ' ':
case 't':
words += 1;
default:
chars += 1;;
}
表示执行流此时将贯穿case标签。事实上需要让switch语句贯穿case标签的情况非常罕见,因此当真正出现这种情况时,花点力气写条注释,以后再维护程序时可能会节省很多的时间。
#include
#include
int main( void ){
int ch;
int lines = 0;
int words = 0;
int chars = 0;
while( (ch = getchar()) != EOF ){
switch( ch ){
case 'n':
lines += 1;
case ' ':
case 't':
words += 1;
default:
chars += 1;
break;
}
}
printf( "lines = %d, words = %d, chars = %dn", lines, words, chars );
return EXIT_SUCCESS;
}



