首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

3.2  多项选择问题

在编程时,常常会遇到多项选择问题。例如根据候选人是否来自6所不同大学中的一所,来选择一组不同的动作。另一个例子是根据某一天是星期几,来执行某组语句。在C语言中,有两种方式处理多项选择问题。一种是采用else-if形式的if语句,这是处理多项选择的最常见方式。另一种是switch语句,它限制了选择某个选项的方式,但在使用switch语句的场合中,它提供了一种非常简洁且便于理解的解决方案。下面先介绍else-if语句。

3.2.1  给多项选择使用else-if语句

从一组选项中选择一项的else-if语句如下:

if(choice1)

/* Statement or block for choice 1 */

else if(choice2)

/* Statement or block for choice 2 */

else if(choice3)

/* Statement or block for choice 2 */

/* … and so on … */

else

/* Default statement or block */

每个if表达式可任意组成,只要其结果是true或false即可。如果第一个if表达式choice1是false,就执行下一个if。如果choice2是false,就执行下一个if。继续下去,直到找到一个结果为true的表达式为止。此时,就执行该if中的语句或语句块。然后结束这个执行序列,执行else-if语句后面的语句块。

如果所有的if条件都是false,就执行最后一个else后面的语句或语句块。可以忽略这个else,此时,如果所有的if条件都是false,这个else-if语句序列就什么也不做。下面是一个例子:

if(salary<5000)

printf("Your pay is very poor.");    /* pay < 5000 */

else if(salary<15000)

printf("Your pay is not good.");     /* 5000 <= pay < 15000 */

else if(salary<50000)

printf("Your pay is not bad.");      /* 15000 <= pay < 50000 */

else if(salary<100000)

printf("Your pay is very good.");    /* 50000 <= pay < 100000 */

else

printf("Your pay is exceptional."); /* pay > 100000 */

注意,在第一个if语句后,不需要测试if条件中的下限,因为如果执行到某个if,前面的测试就一定是false。

任意逻辑表达式都可以用作if条件,所以这个语句非常灵活,可以从任意多个选项中选择一项。switch语句没有这么灵活,但在许多情况下使用起来更简单。下面看看switch语句。

3.2.2  switch语句

switch语句允许根据一个整数表达式的结果,从一组动作中选择一个动作。下面用一个简单的例子来说明其工作原理。假定在一家彩票销售点,数字35可赢得一等奖,数字122可赢得二等奖,数字78可赢得三等奖。使用switch语句可以检查购买彩票者是否获奖:

switch(ticket_number)

{

case 35:

printf("Congratulations! You win first prize!");

break;

case 122:

printf("You are in luck - second prize.");

break;

case 78:

printf("You are in luck - third prize.");

break;

default:

printf("Too bad, you lose.");

}

在关键字switch的后面,括号中表达式的值是ticket_number,它确定执行括号中的哪些语句。如果ticket_number的值与某个case关键字后面的指定值匹配,就执行该case后面的语句。例如,如果ticket_number的值是122,就显示如下信息:

You are in luck - second prize.

printf()后面的break语句的作用是跳过括号中的其他语句,执行闭括号后面的语句。如果省略了某个case后面的break语句,就继续执行下一个case的语句。如果ticket_number的值不对应任何一个case值,就执行default关键字后面的语句,生成默认的信息。default和break都是C语言中的关键字。switch语句的一般形式如下:

switch(integer_expression)

{

case constant_expression_1:

statements_1;

break;

....

case constant_expression_n:

statements_n;

break;

default:

statements;

}

上述代码的测试基于integer_expression的值。如果该值对应于相关值constant_expression_n定义的某个case值,就执行该case值后面的语句。如果integer_expression的值不同于所有的case值,就执行default后面的语句。我们无法选择多个case,所以所有的case值都必须互不相同。否则,在编译程序时就会得到一个错误信息。case值必须是常量表达式,即可以在编译期间计算的表达式,这意味着case值不能依赖程序执行时确定的值。当然,测试表达式可以是任意的,只要它等于某个整数即可。

可以忽略default关键字及其相关的语句。如果没有case值匹配,就什么也不做。但要注意,constant_expression对应的所有case值必须互不相同。break语句会跳转到闭括号后面的语句上。

注意标点符号和格式。在第一个switch表达式的结尾处没有分号。语句体用括号括起来。case的constant_expression值后跟一个冒号,后面的每条语句都以分号结束,这与一般的语句相同。

enumeration类型是整数类型,所以可以使用enumeration类型的变量控制switch。下面是一个例子:

enum Weekday {Monday, Tuesday, Wednesday,

Thursday, Friday, Saturday, Sunday};

enum Weekday today = Wednesday;

switch(today)

{

case Sunday:

printf("Today is Sunday.");

break;

case Monday:

printf("Today is Monday.");

break;

case Tuesday:

printf("Today is Tuesday.");

break;

case Wednesday:

printf("Today is Wednesday.");

break;

case Thursday:

printf("Today is Thursday.");

break;

case Friday:

printf("Today is Friday.");

break;

case Saturday:

printf("Today is Saturday.");

break;

}

这个switch语句选择对应于today变量值的case,在本例中,显示的信息是“Today is Wednesday”。在这个switch语句中没有默认的case,但可以添加一个,以防止出现today的无效值。

可以把多个case值与一组语句联系起来。还可以使用计算结果为char值的表达式作为switch的控制表达式。假定从键盘上将一个字符读入char类型的变量ch中,在switch语句中测试这个字符,如下所示:

switch(tolower(ch))

{

case 'a': case 'e': case 'i': case 'o': case 'u':

printf("The character is a vowel.");

break;

case 'b': case 'c': case 'd': case 'f': case 'g': case 'h': case 'j':

case 'k':case 'l': case 'm': case 'n': case 'p': case 'q': case 'r':

case 's': case 't': case 'v': case 'w': case 'x': case 'y': case 'z':

printf("The character is a consonant.");

break;

default:

printf("The character is not a letter.");

break;

}

这里使用了在<ctype.h>头文件中声明的tolower()函数,将ch的值转换为小写,所以只需测试小写字母。如果ch包含的字符码表示一个元音,就输出一条信息。有5个case值对应元音,对它们要执行相同的printf()语句。同样,当ch包含辅音时,也输出一条相应的信息。如果ch包含的字符码既不是辅音,也不是元音,就执行默认的case。

注意,默认case后面的break语句,它不是必要的,但也是有作用的。总是把一个break语句放在最后一个case的末尾,可以确保以后在末尾添加一个新的case时,switch总是正确工作。

使用另一个在<ctype.h>头文件中声明的isalpha()函数,可以简化这个switch。如果作为参数传入的字符是字母,isalpha()函数就返回一个非零整数(true),否则就返回0(false)。因此,下面的代码会生成与前面switch相同的结果:

if(!isalpha(ch))

printf("The character is not a letter.");

else

switch(tolower(ch))

{

case 'a': case 'e': case 'i': case 'o': case 'u':

printf("The character is a vowel.");

break;

default:

printf("The character is a consonant.");

break;

}

if语句测试ch是否不是字母,如果不是,就输出一条信息。如果ch是一个字母,switch语句就测试它是元音还是辅音。5个元音case值会生成一个结果,默认的case生成另一个结果。在执行switch语句时,ch包含的是一个字母,所以如果ch不是元音,就一定是辅音。

除了前面介绍的tolower()、toupper()和isalpha()函数之外,<ctype.h>头文件还声明了其他几个函数来测试字符,如表3-4所示。

表3-4  测试字符的函数

函    数

测 试 内 容

islower()

小写字母

isupper()

大写字母

isalnum()

大写或小写字母

iscntrl()

控制字符

isprint()

可打印字符,包括空格

isgraph()

可打印字符,不包括空格

isdigit()

十进制数字('0'~'9')

isxdigit()

十六进制数字('0'~'9','A'~'F', 'a'~'f'))

isblank()

标准空白字符(空格,'\t')

isspace()

空位字符(空格,'\n', '\t', '\v', '\r', '\f')

ispunct()

Isspace() 和isalnum()返回false的可打印字符

如果这些函数找到了它们希望的字符,就返回一个非零整数(表示true),否则返回0(false)。

下面用一个例子来演示switch语句。

试试看:选择幸运数字

这个例子假定,在抽奖活动中有三个幸运数字,参与者要猜测一个幸运数字,switch语句会结束这个猜测过程,给出参与者可能赢得的奖励。

/* Program 3.8 Lucky Lotteries */

#include <stdio.h>

int main(void)

{

int choice = 0; /* The number chosen */

/* Get the choice input */

printf("\nPick a number between 1 and 10 and you may win a prize! ");

scanf("%d",&choice);

/* Check for an invalid selection */

if((choice>10) || (choice <1))

choice = 11; /* Selects invalid choice message */

switch(choice)

{

case 7:

printf("\nCongratulations!");

printf("\nYou win the collected works of Amos Gruntfuttock.");

break; /* Jumps to the end of the block */

case 2:

printf("\nYou win the folding thermometer-pen-watch-umbrella.");

break; /* Jumps to the end of the block */

case 8:

printf("\nYou win the lifetime supply of aspirin tablets.");

break; /* Jumps to the end of the block */

case 11:

printf("\nTry between 1 and 10. You wasted your guess.");

/* No break - so continue with the next statement */

default:

printf("\nSorry, you lose.\n");

break; /* Defensive break - in case of new cases */

}

return 0;

}

这个程序的输出如下:

Pick a number between 1 and 10 and you may win a prize! 3

Sorry, you lose.

或:

Pick a number between 1 and 10 and you may win a prize! 7

Congratulations!

You win the collected works of Amos Gruntfuttock.

如果输入无效数字:

Pick a number between 1 and 10 and you may win a prize! 92

Try between 1 and 10. You wasted your guess.

Sorry, you lose.

代码的说明

开始的代码与前面的程序相同,也是声明一个整型变量choice,接着要求用户输入一个1~10之间的数字,把该值存储在choice中:

int choice = 0; /* The number chosen */

/* Get the choice input */

printf("\nPick a number between 1 and 10 and you may win a prize! ");

scanf("%d",&choice);

在执行其他操作之前,检查用户是否输入了一个1~10之间的数字:

/* Check for an invalid selection */

if((choice>10) || (choice <1))

choice = 11;                   /* Selects invalid choice message */

如果值不在1~10之间,就自动把它改为11,这不是必要的,但为了确保用户不出错,把choice变量设置为11,对于这个case值,printf()语句会生成错误信息。

接着是switch语句,它根据choice的值从括号之间的case中选择:

switch(choice)

{

...

}

如果choice的值是7,就执行该值对应的case:

case 7:

printf("\nCongratulations!");

printf("\nYou win the collected works of Amos Gruntfuttock.");

break;         /* Jumps to the end of the block */

执行两个printf()调用,之后break语句跳到闭括号后面的语句上(这里是结束程序,因为闭括号后面没有语句了)。

下面的两个case也是这样:

case 2:

printf("\nYou win the folding thermometer-pen-watch-umbrella.");

break;      /* Jumps to the end of the block */

case 8:

printf("\nYou win the lifetime supply of aspirin tablets.");

break;     /* Jumps to the end of the block */

它们对应于choice的值是2或8的情况。

下一个case有点不同:

case 11:

printf("\nTry between 1 and 10, you wasted your guess.");

/* No break – so continue with the next statement */

它没有break语句,所以在显示了信息后,继续执行默认case的printf()。其结果是,如果choice设置为11,会得到两行输出。这对于本例完全合适,但一般应在每个case的最后添加break语句。从程序中删除break语句,再输入7,看看结果如何。每个case都会得到所有的输出信息。默认case如下:

default:

printf("\nSorry, you lose.\n");

break;        /* Defensive break - in case of new cases */

如果choice的值不对应任何一个case值,就选择这个默认case。这里也有一个break语句,尽管它是不必要的,但许多程序员仍总是把break语句放在默认case语句的后面,或者switch语句的最后一个case后面,这便于以后提供更多的case语句。如果忘记在默认case的后面加上break语句,switch语句就不会按照希望的那样执行。switch语句中的case顺序可任意,default不一定是最后一个case。

试试看:是或否

下面的switch语句由用户输入的char变量值来控制。程序提示用户为一个动作输入值'y'或'Y',为另一个动作输入'n'或'N'。这个程序其实没有什么用,但许多程序常常会问一个问题,再执行每个动作(例如保存一个文件)。

/* Program 3.9 Testing cases */

#include <stdio.h>

int main(void)

{

char answer = 0;      /* Stores an input character */

printf("Enter Y or N: ");

scanf(" %c", &answer);

switch(answer)

{

case 'y': case 'Y':

printf("\nYou responded in the affirmative.");

break;

case 'n': case 'N':

printf("\nYou responded in the negative.");

break;

default:

printf("\nYou did not respond correctly...");

break;

}

return 0;

}

这个程序的输出如下:

Enter Y or N: y

You responded in the affirmative.

代码的说明

把answer变量声明为char类型时,还把它初始化为0。接着要求用户输入一个值,并存储该值:

char answer = 0;        /* Stores an input character */

printf("Enter Y or N: ");

scanf(" %c", &answer);

switch语句使用存储在letter中的字符选择case:

switch(answer)

{

...

}

switch语句中的第一个case要求用户输入Y的大写字母或小写字母:

case 'y': case 'Y':

printf("\nYou responded in the affirmative.");

break;

输入值'y'和'Y',都会执行相同的printf()。通常,可以把任意多个这样的case组合在一起。注意其标点符号:两个case放在一起,用一个冒号隔开。

否定的输入以相同的方式处理:

case 'n': case 'N':

printf("\nYou responded in the negative.");

break;

如果输入的字符不对应所有的case值,就选择默认的case:

default:

printf("\nYou did not respond correctly...");

break;

注意默认case的printf()语句后面的break语句以及合法的case值。与以前一样,break语句会使执行过程在此中断,并从switch语句后面的语句继续执行。另外,没有break语句,会执行后续case中的语句,除非有效case的前面有break语句,否则就会执行后面的语句(包括default语句)。

当然,也可以使用toupper()或tolower()函数简化switch中的case。使用这两个函数之一,可以使case个数减半:

switch(toupper(answer))

{

case 'Y':

printf("\nYou responded in the affirmative.");

break;

case 'N':

printf("\nYou responded in the negative.");

break;

default:

printf("\nYou did not respond correctly...");

break;

}

如果使用toupper()函数,就需要用#include指令包含<ctype.h>。

3.2.3  goto语句

If语句允许根据测试的结果选择执行两个语句块中的一个。这是一个强大的工具,可以改变程序的自然执行顺序,程序不再从A执行到B,再到C和D,而可以执行到A,再决定是否跳过B和C,直接执行D。

goto语句是一个比较生硬的指令,它可以无条件地改变程序流——不必通过Go,也不必缴纳$200,而是直接进监狱。程序在遇到goto语句时,也会无条件地跳转。goto语句会直接跳到某个指定的位置,无须检查任何值,或者要求用户考虑这是否是他希望执行的操作。

这里仅简要介绍goto语句,因为它并不像初看起来那么强大。goto语句的问题是它看起来太简单了,这似乎不太恰当,但重要的是“看起来”这个词。goto语句很简单,可以使用它到达任何地方,其实此时使用另一个语句会更好。goto语句会产生非常难以理解的代码。

在使用goto语句时,会跳转到代码中用语句标签指定的位置。语句标签的定义方式与变量名相同,也是一组字母和数字,其中第一个字符必须是字母。语句标签后跟一个冒号(:),将它与它标记的语句分开。它看起来类似于switch中的case标签。case标签就是语句标签。

与其他语句一样,goto语句也用分号结束:

goto there;

目标语句必须有与goto语句相同的标签,在上面的例子中,该标签是there。如前所述,标签写在它所应用的语句之前,其后的冒号将该标签和语句的其他部分隔开,如下面的例子所示:

there: x=10;        /* A labeled statement */

goto语句可以与if语句一起使用,如下面的例子所示:

...

if(dice == 6)

goto Waldorf;

else

goto Jail;        /* Go to the statement labeled Jail */

Waldorf:

comfort = high;

...

/* Code to prevent falling through to Jail */

Jail:              /* The label itself. Program control is sent here */

comfort = low;

...

这段代码在掷骰子。如果掷出了6,就跳转到Waldorf,否则就跳转到Jail。这似乎很不错,但它很容易出现混淆。要理解执行的顺序,需要找出目标标签。假定代码使用了许多goto语句,就很难理解,甚至在出错时都无法修改。所以应尽可能避免使用goto语句。在理论上,总是可以避免使用goto语句,但在一两种情况下,这是一个有用的选项。第4章在介绍循环时会提到,从嵌套了许多层循环的最内层中退出时,使用goto语句比采用其他机制简单得多。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论