VC++ 2008专题应用程序开发实例精讲
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.6 控制语句

1.6.1 if语句

如果给定的条件是true,基本的if语句允许程序执行一条语句或一条包含在花括号中的语句块,如图1-1所示。

图1-1 if语句执行情况

下面是if语句的一个简单例子,它测试类型为char的变量letter的值:

if(letter == ' A' )
std::cout << "The first capital, alphabetically speaking.\n";
std::cout << "This statement always executes.\n";

如果letter的值是’A' ,条件就为true,将输出下面的结果:

The first capital, alphabetically speaking.
This statement always executes.

如果letter的值不是’A' ,就只输出第二行语句。要测试的条件放在关键字if后面的括号中。注意分号的位置,它位于if和括号中的条件后面的语句之后。在括号中的条件的后面不能有分号,因为if和条件是和后面的语句或语句块绑定在一起的,它们本身是不能单独存在的。

if之后的语句只有在条件为true时才执行。对于程序的编译来说,语句的缩进是不必要的,但这种缩进有助于理解if条件和依赖它的语句之间的关系。有时,简单的if语句还可以写在一行上:

if (letter == ' A' ) std::cout << "The first capital, alphabetically speaking.\n";

一般情况下,最好把语句(或语句块)和if条件放在不同的代码行上,这样会更清楚。

扩展上面的例子,如果letter的值是’A' ,就改变它的值:

if(letter == ' A' ) {
std::cout << ' The first capital, alphabetically speaking.\n";
letter = ' a' ;
}
std::cout << "This statement always executes.\n";

在if条件为true时,就执行块中的所有语句。如果没有加上花括号,则只有第一个语句是if块的内容,而给letter赋予’a’的语句将总是执行。注意在块中每个语句的最后都有一个分号,而在块结束的右花括号后面没有分号。在块中可以放置任意多个语句,甚至还可以嵌套块,因为letter的值是’A' ,所以块中的两个语句都会执行,在输出与前面相同的消息后,它的值将变为’a' 。如果条件为false,将不执行这两个语句。当然,if块后面的语句总是执行。

程序示例——下面试用if语句,创建一个程序,检查从键盘上输入的一个整数值:

//程序的功能是演示使用if
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main() {
cout << " Enter an integer between 50 and 100: ";
int value=0;
cin>> value;
if(value<50)
cout << " The value is invalid - it is less than 50."<<endl;
if(value>100)
cout << " The value is invalid - it is greater than 100."<<endl;
cout << " You entered"<<value<<endl;
return 0;
}

输出的值取决于输入的值,对于值在50~100之间的数据,应输出如下语句:

Enter an integer between 50 and 100: 77
You entered 77

如果输入的值在50~100的范围之外,就给出一个消息,说明值是无效的,并显示该值。如果该值小于50,则输出如下所示:

Enter an integer between 50 and 100: 27
The value is invalid - it is less than 50.
You entered 27

如果该值大于100,则输出如下所示:

Enter an integer between 50 and 100: 270
The value is invalid - it is greater than 100.
You entered 270

对该例的说明如下。

在给出提示,并读取一个值后,第一个if语句就会检查输入的值是否小于下限50:

if(value<50)
cout << " The value is invalid - it is less than 50."<<endl;

只有if条件为true时,也就是当value小于50时,才执行以上输出语句。下一个if语句检查上限:

if(value>100)
cout <<" The value is invalid - it is greater than 100."<<endl;

如果value大于100,就执行该输出语句。最后一个输出语句是:

cout << " You entered"<<value<<endl;

这个语句总是执行。

1.6.2 嵌套的if语句

在if语句中的条件为true时才执行的语句本身也可以是一个if语句,这种情况称为嵌套的if语句。这种情况下只有外层if的条件为true时,才测试内层if的条件。嵌套在一个if语句中的if语句也可以包含另一个嵌套的if语句。一般情况下,可以继续嵌套if语句,嵌套的次数没有限制。

1.程序示例——使用嵌套的if语句

下面用一个工作示例来演示嵌套的if语句。该示例测试从键盘上输入的字符是否为字母。这个示例很好地使用了嵌套的if语句,但其中一些固有的假设最好避免,读者可以试着找出这些假设。下面是具体代码:

//程序的功能是演示使用嵌套if
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main() {
char letter=0; //此变量存储的是输入值
cout << "Enter a letter: "; //输入提示
cin >> letter; //读入一个字符
if(letter >= ' A' ) { //是否等于’A'
if(letter <= ' Z' ) { //是否等于’Z'
cout << "You entered an uppercase letter."
<< endl;
return 0;
}
}
if(letter >= ' a' ) //是否等于’a'
if(letter <= ' z' ) { //是否等于’z'
cout << "You entered an lowercase letter."
<< endl;
return 0;
}
cout<<" You do not enter a letter."<<endl;
return 0;
}

本例的输出如下所示:

Enter a letter: H
You entered an uppercase letter.

本例的说明如下。

这个程序首先是通常的注释行和支持输入输出的头文件<iostream>的#include语句,以及程序中std名称的using声明。在为char变量letter分配内存空间并初始化为0后,函数main()将提示输入一个字母。

之后的if语句检查输入的字符是否为’A’或更大的字母:

if (letter >= ' A' ) { //是否等于’A'
if (letter <= ' Z' ) { //是否等于’Z'
cout << "You entered an uppercase letter."
<< endl;
return 0;
}
}

如果letter大于或等于’A' ,嵌套的if就检查输入的字符是否为’Z’或更小的字母。如果该字母是’Z’或更小的字母,就说明该字符是一个大写字母,于是显示一个消息,然后执行return语句,结束程序。因为这两个语句都放在花括号中,所以在嵌套的if条件为true时,这两个语句都会被执行。

这对嵌套的if语句建立在用编码表示字母字符的两个假设的基础上。第一个假设是字母’A'到’Z’用一组编码表示,其中’A’的编码最小,' Z’的编码最大。第二个假设是大写字母的编码是连续的,在’A’编码和’Z’编码之间不存在非字母字符。在代码中建立这样的假设并不好,因为这限制了程序的可移植性。例如,在EBCDIC编码中,字母的字符编码就是不连续的。稍后将介绍如何避免这种限制。

下一个if语句使用了与第一个if语句相同的机制,光检查输入的字符是否为小写,然后显示一个消息并返回。

if (letter >= ' a' ) //是否等于’a'
if (letter <= ' z' ) { //是否等于’z'
cout << "You entered an lowercase letter."
<< endl;
return 0;
}

如果仔细检查,就会注意到小写字符的测试只包含一对花括号,而大写字母的测试包含两对花括号。这里花括号中的代码块属于内层的if语句。实际上,这两种方式都是对的。在C++中, if(condition){…}是一个语句,不需要放在花括号中。如果觉得使用较多的花括号能使代码更加清晰,就可以使用它们。同时,与大写字母测试一样,这些代码隐含了小写字母编码的假设。

只有在输入的字符不是字母时,才执行最后一个if语句块后面的输出语句,它会显示一个消息,然后执行return语句。嵌套的if语句和输出语句之间的关系更容易理解,因为每个if语句块都实现了缩进。在C++中,缩进格式通常用于提供程序逻辑的可视化线索。

如本例开头所述,该程序演示了嵌套的if语句块如何工作,但这并不是测试字符的好方法。使用标准库可以编写出独立于字符编码的程序,下面就介绍这部分内容。

2.不依赖编码的字符处理

标准库提供了许多函数,它们可以在程序中执行许多任务,表1-6列出了这些函数。在程序中包含<cctype>头文件之后,就可以访问一组非常有用的函数集来测试字符。在测试字符时,需要给函数传送一个int类型的变量或常数量。如果传送了char类型的值,编译器会把它自动转换为int类型。

表1-6 测试字符的函数

这些函数都返回一个int类型的值。如果字符的类型与要测试的类型相同,该值就为正(true),否则就为0(false)。为什么这些函数不返回bool类型的值呢?后者看上去似乎更有意义。原因是包含这些函数的C标准库是在bool类型引入C++之前建立的。

<cctype>头文件还提供了两个函数,如表1-7所示。在大写字符和小写字符之间转换,传送给这两个函数的字符应是int类型,返回的结果也为int类型。

表1-7 转换字符的函数

可以使用这些函数实现前面的例子,而无须任何字符编码的假设。不同环境中的不同字符编码总是由标准库函数来考虑的,用户不需要考虑。因为使用标准库函数后,也不需要使用嵌套的if语句,所以代码要比前面简单得多。

注意所有这些字符测试函数,除isdigit()和isxdigit()之外,都在当前环境下测试变元,本地环境确定了本地数据是如何处理的。不同的国家使用不同的字符集表示字母,所以某个字符编码是否解释为字母取决于本地环境。货币单位和小数的显示方式也随着本地环境的不同而不同。调用在<clocale>头文件中声明的setlocale()函数可以设置本地环境。这个函数接受两个变元:第一个变元指定应用本地环境的函数的类别,第二个变元指定本地环境。用于第一个变元的值必须是在<clocale>头文件中声明的值,表1-8列出了这些值。

表1-8 受本地环境影响的类别值

setlocale()的第二个变元是一个指定本地环境的字符串。字符串“C”是默认值,对应于拉丁字母‘A’到‘Z' 。可以用于指定其他本地环境的字符串集是由实现方式定义的,通常包括简单明了的国家规范,如Germany。

C++头文件<locale>提供了许多扩展功能的声明,它们可以处理依赖于本地环境的数据,当程序中需要支持多个本地环境时,就应使用它们。

· 程序示例——使用标准库字符转换函数

在使用标准库函数修改上一个例子时,还可以扩展程序的功能,例如使用转换函数:

//程序的功能是演示使用标准字符转换函数
#include <cctype> // 字符测试和转化
using std::cin;
using std::cout;
using std::endl;
int main() {
char letter=0; // 存储输入字符
cout<<endl
<< "Enter a letter: "; // 提示输入
cin>> letter; // 输入字符
cout<<endl;
if (std::isupper(letter)) { //大写字符测试
cout << "You entered a capital letter."
<<endl;
cout << " Converting to lowercase we get "
<<static_cast<char>( std::tolower(letter))<<endl;
return 0;
}
if (std::islower(letter)) { //小写字符测试
cout << "You entered a small letter."
<<endl;
cout << "Converting to uppercase we get "
<< static_cast<char>( std::toupper(letter)) <<endl;
return 0;
}
cout<<" You id not enter a letter."<<endl;
return 0;
}

该例的输出如下所示:

Enter a letter: t
You entered a small letter.
Converting to uppercase we get T

该例的说明如下。

if表达式已改为使用标准库函数,不再需要嵌套的if语句,因为前面要测试的两个条件现在都包含在isupper()或islower()函数中了。

用户并不需要关心这些函数的工作原理。要使用它们,只需要知道它们完成什么任务,需要给它们传送多少参数,传送什么类型的参数,以及它们返回什么类型的值。有了这些信息,就可以使用标准库函数,使代码更简单、更一般化。程序的这个版本可以处理使用任何字符编码的char类型。

注意,在输出语句中可以直接使用从转换函数中返回的结果,如下所示:

cout << "Converting to upper case we get "
<< static_cast<char>(std::toupper(letter)) <<endl;

由于toupper()函数返回的值是int类型,因此这里把它强制转换为char类型,并发送给输出流cout。如果要存储返回的字符,而不是进行显式强制转换,就可以将它存储在原来的变量letter中,如下面的语句所示:

letter =std::toupper(letter);

接着就可以在输出语句中使用变量letter输出转换后的字符了:

cout << "Converting to uppercase we get "<<letter <<endl;

如果需要使用多字节字符(其类型是wchar_t),就可以包含头文件<cwctype>。该文件包含了在<cctype>中声明的所有函数的对应多字节字符。每个测试函数名都在is的后面加上了w,所以它们的名称就变成:

iswupper() iswdigit() iswspace() iswgraph()
iswlower() iswxdigit() iswcntrl() iswpunct()
iswalpha() iswalnum() iswprint()

它们都传递多字节字符参数,并且返回一个int值,就像处理char类型的字符函数一样。同样,多字节字符转换函数也称为towupper()和towlower()。

注意 C++中的<cctype>和<cwtype>头文件继承于C。在许多实现方式中,函数在std命名空间内部和外部都定义了,以允许旧式C程序编译和链接。此时,函数名无论是否带std限定符都能工作,但因为编写的是C++程序,所以应限定名称。

下面使用的if语句在指定的条件为true时执行一个语句;接着,程序按顺序执行下一个语句。当然,也有时候希望只有在条件为false时才执行某个语句或语句块。为此,就要扩展if语句,允许在条件为true时执行一组动作,在条件为false时执行另一组动作。之后,程序按顺序执行下一条语句。这可以描述为if-else语句。

if-else组合提供了两个选项供选择,其一般逻辑如图1-2所示。

图1-2 if-else语句执行情况

图1-2中的流程图指出了语句的执行顺序取决于if条件为true还是false。如图1-2所示,在可以使用语句的地方,总是可以用一条语句块来代替。这表示可以为if-else语句的每个选项执行任意多条语句。

下面仍然使用char类型的变量,编写一个if-else语句,报告存储在变量letter中的字符是否为字母或数字:

if(std::isalnum(letter))
std::cout << "It is a letter or a digit." << std::endl;
else
std::cout << "It is neither a letter nor a digit." << std::endl;

这个程序使用了<cctype>头文件中的函数isalnum()。如果变量letter包含字母或数字,函数isalnum()就返回一个正整数。因为if语句把这个看做是true,所以显示第一个消息。如果变量letter包含的不是字母或数字,函数isalnum()就返回0。对于if来说,这会自动转换为false,执行else之后的输出语句。

· 程序示例——扩展if语句

下面用一个例子来演示if-else语句,这次测试的是数值:

//程序的功能是演示使用if-else语句
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main() {
long number=0; //存储字符
cout << "Enter an integer less than 2 billion: ";
cin >> number ;
cout << endl;
if (number % 2L ==0) //能否被2整除
cout << " \n Your number is even." //是否为零
<< endl;
else
cout << " \n Your number is odd." //是否为1
<< endl;
return 0;
}

这个程序的输出如下所示:

Enter an integer less than 2 billion: 123456
Your number is even.

该例的说明如下。

在把输入的值读入number之后,就在if条件中测试该数除以2的余数(使用第2章中介绍的取余运算符%),检查它是否为0。整数除以2后的余数只能是1或0。程序中的代码进行了注释,如果余数等于0, if条件就是true,则执行if之后的语句。如果余数等于1,则if条件为false,就执行else关键字后面的语句。在输出结果后,执行return语句,结束程序。

提示 else关键字的后面没有分号,这与语句中的if一样。这里也采用了缩进格式,作为各个语句之间关系的可视化指示符。读者可以清楚地看出哪个语句在得到true时执行,哪个语句在false时执行。在程序中,应尽量缩进语句,以显示它们的逻辑结构。

在这个例子中,演示了编写if条件的另一种方式。在转换为bool类型时,任何非0值都是true,而0值将被转换为false。所以,可以把模式化操作的结果用做条件,而不需要比较它和0值。这样,if-else语句就变成:

if(number % 2L) //能否被2整除
cout << " Your number is odd."//是否为1
<< endl;
else
cout << " Your number is even."//是否为零
<< endl;

if和else子句需要调换,因为如果number的值是偶数,则(number % 2L == 0)就返回true,而(number % 2L)会转换为false。初看起来这似乎有点让人迷惑,但这个条件的第一个版本如下:

“余数为0是true吗?”

由于1会转换为true,因此,该条件的第二个版本应如下:

“余数是1吗?”

1.6.3 嵌套的if-else语句

前面介绍了如何在if语句中嵌套if语句。显然,也可以在if语句中嵌套if-else语句,在if-else语句中嵌套if语句,或者在if-else语句中嵌套其他if-else语句,这样就提供了极大的灵活性(同时也很容易出现混淆)。下面举几个例子。先看第一种情况,即在if语句中嵌套if-else语句:

if(coffee==' y' )
if(donuts==' y' )
std::cout << " We have coffee and donuts ."
<< std::endl;
else
std::cout << " We have coffee, but not donuts."
<< std::endl;

其中,coffee和donuts是char类型的变量,其值分别是’y’和’n' 。由于对donuts的测试仅在coffee测试的结果为true时才进行,因此在每种情况下,消息都会反映正确的情形。else属于donuts测试中的if语句,这很容易引起混淆。

如果编写这些代码时,缩进格式有错误,就会推导出错误的结论:

if(coffee==' y' )
if(donuts==' y' )
std::cout << std::endl
<< "We have coffee and donuts .";
else //这个else不正确
std::cout << " We have no coffee…" //错误
<< std::endl;

代码的缩进让人错误地认为if语句嵌套在if-else语句中,实际上并非如此。第一个消息是正确的,但执行else后的结果就是错误的。这个语句仅在对coffee的测试为true时才执行,因为else属于donuts的测试,不属于coffee的测试。这个错误虽然很容易看出来,但需要注意的是if结构越大就越复杂,就越需要弄清楚哪个if拥有哪个else。

注意 else总是属于前面最接近的那个if(只要另一个else还不属于这个if)。这种混淆称为else悬挂问题。

在程序中,只要一组if-else语句看起来有些复杂,就可以应用这个规则,对该组语句进行排序。在编写程序时,应尽量使用花括号,使代码更清晰。在上例这样简单的情形中不需要使用花括号,但可以将其改写为:

if(coffee==' y' ) {
if(donuts==' y' )
std::cout << "We have coffee and donuts ."
<< std::endl;
else
std::cout << " We have coffee, but not donuts."
<< std::endl;
}

现在代码非常清晰了,else肯定属于测试donuts的if语句。

知道了规则后,理解if语句嵌套在if-else语句的情形就比较容易了。

if(coffee==' y' ) {
if(donuts==' y' )
std::cout << " We have coffee and donuts ."
<< std::endl;}
else if(tea==' y' )
std::cout << " We have no coffee, but we have tea"
<< std::endl;

提示 注意这里的代码格式。一个if语句嵌套在else的下面,此时可以把else和if写在一行上。这里花括号是必不可少的,如果省略了花括号,else就属于测试donuts的if语句了。在这种情况下,编程人员很容易忘记加上花括号,从而生成一个很难找出的错误。有这种错误的程序也会通过编译,因为代码是完全正确的。有时甚至结果也是正确的,但它没有表达出真正的意图。

如果在上例中删除花括号,则只要coffee和donuts都等于’y' ,就不会执行if(tea == ' y' )检查,从而得到正确的结果。

最后,看一个if-else语句嵌套在另一个if-else语句中的情况。即使只有一层嵌套,也可能非常混乱。最好对coffee和donuts进行彻底的分析,再开始使用这种嵌套。

if(coffee==' y' )
if(donuts==' y' )
std::cout<< " We have coffee and donuts ."
<< std::endl;
else
std::cout<< " We have coffee, but not donuts."
<< std::endl;
else if(tea==' y' )
std::cout<< " We have no coffee, but we have tea, and maybe donuts…"
<< std::endl;
else
std::cout<< "No tea or coffee, but maybe donuts…"
<< std::endl;

即使采用了正确的缩进格式,这里的逻辑看起来也不是很明显。这里不需要使用花括号,因为前面的规则已校验,但如果加上花括号,则看起来会更清楚一些:

if(coffee==' y' ) {
if(donuts==' y' )
std::cout<< " We have coffee and donuts ."
<< std::endl;
else
std::cout << " We have coffee, but not donuts"
<< std::endl;
}
else
{
if(tea==' y' )
std::cout<< " We have no coffee, but we have tea, and maybe donuts…"
<< std::endl;
else
std::cout<< " No tea or coffee, but maybe donuts…"
<< std::endl;
}

如果把足够多的嵌套if语句放在一起,很可能会出错。在程序中处理这种逻辑还有更好的方式。

1.6.4 switch语句

用户常常会面临多项选择的情形,在这种情况下,需要根据整数变量或表达式的值,从许多选项(多于两个)中确定执行哪个语句集。例如抽奖,顾客购买了一张有号码的彩票,如果运气好,就会赢得大奖。例如,如果彩票的号码是147,就会赢得头等奖。如果彩票的号码是387,就会赢得二等奖。如果彩票的号码是29,就会赢得三等奖。其他号码则不能获奖。处理这类情形的语句称为switch语句。

switch语句允许根据给定表达式的一组固定值,从多个选项中进行选择,这些选项称为case。在彩票例子中,有4个case,每个case对应于一个获奖号码,再加上一个默认的case,用于所有未获奖的号码。下面为编写一个switch语句,为给定的彩票号码选择反馈消息:

switch(ticket_number) {
case 147:
std::cout<<"You win first prize! ";
break;
case 387:
std::cout<<"You win second prize! ";
break;
case 29:
std::cout<<"You win third prize! ";
break;
default:
std::cout<<"Sorry, you lose.";
}

switch语句描述起来比其使用难一些。在许多case中如何选择取决于关键字switch后面括号中整数表达式的值。选择表达式的结果也可以是已枚举的数据类型,因为这种类型的值可以自动转换为整数。在本例中,它就是变量ticket_number,必须是整数类型。

可以根据需要,使用多个case值定义switch语句中的所有可能选项。case值显示在case标签中,其形式如下所示:

case case_value:

称其为case标签,是因为它标注了后面的语句。如果选择表达式的值等于case值,就执行该case标签后面的语句。每个case值都必须是唯一的,但不必按特定的顺序,如本例所示。

case值必须是整数常量表达式,即编译器可以计算的表达式,所以它只能使用字面量、const变量或枚举成员。而且,其所包含的所有字面量都必须是整数类型,或者可以强制转换为整数类型。

例子中的default标签标识默认的case,它是一个否则模式。如果选择表达式不对应于任何一个case值,就执行该默认case后面的语句。但是,不一定要指定默认case,如果没有指定它,且没有选中任何case值,switch语句就什么也不做。

从逻辑上看,每一个case语句后面的break语句是绝对必须的,它在case语句执行后跳出switch语句,使程序继续执行switch右花括号后面的语句。如果省略了case后面的break语句,就将执行该case后面的所有语句。注意在最后一个case后面(通常是默认case)不需要break语句,因为此时程序将退出switch语句,但加上break是一个很好的编程习惯,因为这可以避免以后添加另一个case而导致的问题。

提示 switch、case、default和break都是关键字。

· 程序示例——switch语句

下面的例子演示了switch语句的用法:

//这个程序的功能是演示如何使用switch语句
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main() {
int choice=0; //存储选择值
cout << endl
<< "Your electronic recipe book is at your service."<<endl
<< "You can choose from the following delicious dishes: "
<< endl
<< "1 Boiled eggs"<<endl
<< "2 Fired eggs"<<endl
<< "3 Scrambled eggs"<<endl
<< "4 Coddled eggs"<<endl
<< endl<<"Enter your selection number: ";
cin >> choice;
switch(choice) {
case 1:
cout<<endl<<"Boil some eggs."<<endl;
break;
case 2:
cout<<endl<<"Fry some eggs."<<endl;
break;
case 3:
cout<<endl<<" Scramble some eggs."<<endl;
break;
case 4:
cout<<endl<<" Coddle some eggs."<<endl;
break;
default:
cout<<endl<<"You entered a wrong number, try raw eggs."<<endl;
}
return 0;
}

该例的说明如下。

在输出语句中定义选项,并将选中的数字读入变量choice后,就执行switch语句。该语句在关键字switch的后面,把选择表达式指定为括号中的choice。switch语句中的可能选项放在花括号中,每个选项都用一个case标签来标识。如果choice的值对应于某个case值,就执行该case标签后面的语句。在本例中,每个case只有一个语句和break语句,但一般情况下,case标签后面可以有许多语句,且不需要把它们括在花括号中。

每组case语句后面的break语句把执行权传送给switch后面的语句。break语句不是强制的,但如果不加上它,就会执行所选case之后的所有语句,这通常不是人们希望的操作。读者可以把本例中的break语句删除,看会发生什么。

如果choice的值不对应于所指定的所有case值,就执行default标签后面的语句。如果没有包括default case,且choice的值不等于所有的case值,则switch语句就什么也不做,程序继续执行switch后面的语句,即return语句。

· 程序示例——共享case

每个case值都必须是编译时常量,且必须是唯一的。任何两个case值都不能相同的原因是,如果输入了某个指定值,编译器就无法确定应执行哪些语句。但是,case值不同,并不表示必须执行不同的操作。几个case值可以共享相同的操作,如下面的例子所示。

//这个程序的功能是演示使用多值case
#include <iostream>
#include <cctype>
using std::cin;
using std::cout;
using std::endl;
int main() {
char letter=0;
cout << endl
<< " Enter a letter: ";
cin >> letter;
if ( std::isalpha(letter))
switch(std::tolower(letter)) {
case ' a' : case ' e' : case ' i' : case ' o' : case ' u' :
cout<<endl<<" You entered a vowel."<<endl;
break;
default:
cout<<endl<<" You entered a consonant."<<endl;
}
else
cout<<endl<<"You did not enter a letter."<<endl;
return 0;
}

这个程序的输出如下所示:

Enter a letter: E
You entered a vowel.

该例的说明如下。

在这个例子中,使用了标准库的一个字符转换例程和switch语句,来确定输入的字符是元音还是辅音。if条件首先检查是否输入了一个字母,而不是其他字符:

if(std::isalpha(letter))

如果isalpha()返回的值是非0值,就执行以下switch语句:

switch(tolower(letter)) {
case ' a' : case ' e' : case ' i' : case ' o' : case ' u' :
cout<<endl<<" You entered a vowel."<<endl;
break;
default:
cout<<endl<<" You entered a consonant."<<endl;
}

switch由tolower()函数的返回值控制。可以只使用变量letter把所有的大写元音字母指定为case值,也可以把所有的小写元音字母指定为case值。如果tolower()函数返回的值对应于一个元音,就显示确认消息。否则就执行默认的case,显示消息“输入的字符是一个辅音”。

如果isalpha()返回0,就不执行switch语句,而执行else语句,输出消息“输入的字符不是字母”。

可以利用if语句把字母的测试和转换为小写形式这两个操作结合起来,但这需要一些技巧,且会使代码变得比较复杂。例如,可以把switch语句改写为:

switch(std::tolower(letter)*(std::isalpha(letter) ! =0)) {
case ' a' : case ' e' : case ' i' : case ' o' : case ' u' :
cout<<endl<<" You entered a vowel."<<endl;
break;
case 0:
cout<<endl<<"You did not enter a letter."<<endl;
break;
default:
cout<<endl<<" You entered a consonant."<<endl;
}

如前所述,如果给isalpha()函数传送非字母字符,它就会返回整数0;如果给它传送字母,它就会返回一个正整数,但这个正整数不一定是1。选择表达式变复杂的原因,是isalpha()函数不会生成bool值。如果可以生成,就可以使用tolower(letter)*isalpha (letter),当isalpha()返回false时,这个表达式等于0,否则就等于tolower()所返回的小写字母,因为true会转换为1。

另一个方法是把isalpha()返回的值强制转换为bool类型。接着,就可以把switch语句改写为:

switch(tolower(letter)*static_cast<bool>(isalpha(letter))) {
case ' a' : case ' e' : case ' i' : case ' o' : case ' u' :
cout<<endl<<" You entered a vowel."<<endl;
break;
case 0:
cout<<endl<<"You did not enter a letter."<<endl;
break;
default:
cout<<endl<<" You entered a consonant."<<endl;
}

这段代码可以正常执行,因为isalpha()返回的整数被强制转换为bool,编译器会把这个值转换为int,进行乘法运算,所以它最终是0或1。但是,switch语句会变得很混乱。使用if的原始版本肯定是代码最简洁的一个版本,因此是首选的版本,尽管其逻辑不是很好。

1.6.5 while语句

while循环使用逻辑表达式来控制循环体的执行,该循环的一般形式如图1-3所示。

图1-3 while循环的执行过程

这个流程图显示了该循环的逻辑。只要条件的值为true,就执行循环语句或循环语句块。当条件为false时,就执行循环体后面的语句。可以使用任意表达式控制循环,只要该表达式的值为bool类型或整数类型即可。

提示 如果控制循环的条件表达式结果为整数,只要该数值不是0,循环就继续。如前所述,任何非0整数都会被转换为bool类型的true,只有0才被转换为bool类型的false。

当然,while是一个关键字,不能用它来命名程序中的任何元素。

· 程序示例——使用while循环

下面使用while循环计算从1到n的整数和。

//这个程序的功能是演示使用多值while loop
#include <iostream>
#include <iomanip>
using std::cin;
using std::cout;
using std::endl;
int main() {
int n=0;
cout << "How many integers do you want to sum: ";
cin >> n;
int sum=0; //存储求和
int I =1; //存储求和次数
cout << "Values are: "<< endl;
while(i<=n) {
cout << std::setw(5)<<i; //输出i值
if(i%10) ==0)
cout<<endl; //等于10换行
sum += i++;
}
cout<<endl<<"Sum is "<<sum<<endl; //输出sum值
return 0;
}

执行这个程序,输出结果如下所示:

How many integers do you want to sum: 25
Values are:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25
Sum is 325

该例的说明如下。

main()中的前两个语句读取要求和的整数个数。变量n的值用于确定while循环何时结束求和。

在开始循环之前,定义并初始化一个变量i,它存储了当前加到总和中的整数;然后定义变量sum,它存储总和的值。

int sum=0; //存储总和的值
int i=1; //存储当前加入总和的整数

开始循环时,i等于1, sum是0。

循环条件是表达式i<=n,只要i不超过n,它就是true。此时,执行循环语句:

cout <<std::setw(5)<<i; //输出i值
if(i%10) ==0)
cout <<endl; //等于10换行
sum += i++;

首先,输出i的当前值,其字段宽度为5个字符。为了使数字的输出比较整齐,在输出10个数值后输出一个换行符。这样一行有10个数字,只要最大的数值不超过5个数字,所有的数字都可以很好地排列在各个列中。这里最后的一个语句把总和放在sum中。由于使用了递增运算符的后缀形式,因此i的当前值加到sum上,之后递增1。这就执行完了一次循环块,接下来执行传回到while处,再次用i的新值测试循环条件。

这种模式继续重复下去,i递增到2、3、4等,直到n为止。但是,在加上n后,i就递增到n+1,此时循环条件为false,循环停止,程序继续执行循环体后面的语句,即输出总和:

cout<<"Sum is "<<sum<<endl; //输出总值

其结果是循环执行n次,即把从1到n的整数加在一起。

提示 这说明了循环的工作原理,但如果用户喜欢数学,就知道可以用公式n*(n+1)/2来计算整数1到n的总和,实际上并不需要这样一个循环。

do-while循环类似于while循环,只要指定的循环条件为true,循环就将继续执行下去。其区别是在do-while循环中,循环条件是在循环的最后检查,而不是在开始检查,所以循环语句至少要执行一次。

do-while循环的逻辑和一般形式如图1-4所示。特别要注意while语句后面的分号,这是必须的。如果遗漏了它,程序就不会被编译通过。

如果代码块总是要执行一次,也可以执行多次,使用这个逻辑就再合适不过了。下面用一个例子来说明。

图1-4 do-while循环的执行过程

· 程序示例——使用do-while循环控制输入

假定要计算任意个输入值的平均值,这些输入值可以是在某个时间段搜集来的温度。事先无法知道输入多少个值,但可以假定至少会有一个输入值,否则程序就根本不会执行。此时最好使用do-while循环。下面是程序:

//这个程序的功能是演示使用多值 do-while控制输入
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main() {
char ch=0; //存储输入值
int count=0; //计算输入次数
double temperature=0.0; //存储一个临时值
double average=0.0; //存储平均值
cout<<endl;
do {
cout<<"Enter a temperature reading: "; //提示输入t
cin>> temperature; //读出值
average += temperature; //计算临时值
count++; //Increment value count
cout<<"Do you want to enter another? (y/n):";
cin>>ch; //得到请求
cout<<endl;
}while(ch==' y' );
average /= count; //求平均值
cout<<"Average temperature is "<<average
<<endl;
return 0;
}

该程序的运行结果如下所示:

Enter a temperature reading: 53
Do you want to enter another? (y/n): y
Enter a temperature reading: 65.5
Do you want to enter another? (y/n): y
Enter a temperature reading: 74
Do you want to enter another? (y/n): y
Enter a temperature reading: 69.5
Do you want to enter another? (y/n): n
Average temperature is 65.5

该例的说明如下。

首先,程序声明并初始化了循环和计算需要的变量:

char ch=0; //存储输入值
int count=0; //计算输入次数
double temperature=0.0; //存储一个临时值
double average=0.0; //存储平均值

变量ch用于存储对以后输入提示的响应,在循环的最后进行测试。只要输入了y,程序就继续读取输入值(理想情况下,程序还应接受Y,稍后将修正这个错误)。其他三个变量的目的在注释中已说得很清楚。

读取输入值的循环如下所示:

do {
cout<<"Enter a temperature reading: "; //提示输入t
cin>> temperature; //读出值
average += temperature; //计算临时值
count++; //Increment value count
cout<<"Do you want to enter another? (y/n):";
cin>>ch; //得到请求
cout<<endl;
}while(ch==' y' );

因为这里使用的是do-while循环,所以至少要读取一个值。在提示输入后,循环语句块就会从键盘中读取一个值,并把它存储在temperature变量中,接着把这个值加到average上。在循环结束时,average就包含了所有输入值的总和。程序还递增了count,因为需要知道输入了多少个值,才能计算平均值。在循环中,最后提示输入y或n,指出是否还要输入更多的值。在输入n(实际上可以输入除y之外的所有其他字符)后,循环条件ch==' y’就是false,循环终止。程序继续执行下面的语句:

average /= count; //计算平均值

这个语句用在average中累加的总和除以输入值的个数count,得到平均值。存储在count中的值自动转换为double,与average的类型相同,之后执行除法操作。输出结果后,程序结束。

当然,控制while(或do-while)循环时不仅可以使用简单的比较,还可以使用结果为true或false的任何表达式,或者是可以生成一个整数值的任意表达式。前面例子的一个问题是,如果从键盘上输入了Y,而不是y,程序就会终止。这不是很好的编程方式,最好允许输入y和Y以继续循环。为此,只需要把循环条件修改为:

} while(ch==' y' | | ch==' Y' );

这时输入大写的Y或小写的y都可以使循环继续。下面介绍另一种方式。首先,在代码的开始包含头文件,如下所示:

//这个程序的功能是演示使用多值 do-while loop 控制输入
#include <iostream>
#include <cctype>

现在就可以把下面的循环条件放在上面的程序中,以确保用ch的小写版本与y比较:

} while(std::tolower(ch)==' y' );

如前所述,还可以把等于数值的表达式用做循环条件。在这种情况下,编译器会把表达式的结果转换为bool类型。记住,0转换为false,而任何非0值,无论正负,都会被转换为true。因此,只有在条件为0时,用数值控制的while循环才会终止。

1.6.6 for语句

for循环主要用于让语句或语句块执行预定的次数,但也可以用于其他方式。

可以使用以分号分隔开的三个表达式来控制for循环,这三个表达式放在关键字for后面的括号中,如图1-5所示。

图1-5 for循环的控制方式

控制for循环的任一表达式或所有表达式都可以省略,但分号则必须存在。这么做的原因是不明显的,但非常有效,本章后面将探讨省略表达式的一些情况。图1-6显示了for循环的流程逻辑。

图1-6 for循环的流程逻辑

初始化表达式只在循环的开始处计算一次,接着检查循环条件,如果它是true,就执行循环语句或语句块。如果条件是false,就跳过循环语句,执行循环体后面的语句。在这方面,for循环与while循环很相近,与do-while循环则不太相似。

假定条件是true,则执行完循环语句后接着计算迭代表达式,之后再次检查条件,看看是否需继续循环。

· 程序示例——使用for循环

在for循环的一般用法中,第一个表达式用于初始化一个计数器,第二个表达式用于检查计数器是否达到了给定的极限,第三个表达式用于递增计数器。下面是一个例子,它使用for循环来计算整数的总和:

//这个程序的功能是演示使用多值for循环
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main() {
int sum=0; //求和
int count=0; //求和的次数
cout << "How many integers do you want to sum? ";
cin >> count;
for (int i=1; i <=count; i++)
sum +=i;
cout<< endl
<< "The sum of the integers from 1 to "<<count
<< "is "<<sum <<endl;
return 0;
}

这个程序的输出结果如下所示:

How many integers do you want to sum? 25
The sum of the integers from 1 to 25 is 325

该例的说明如下。

用下面的语句可以从键盘上读取要求和的整数的上限:

cout<<" How many integers do you want to sum? ";
cin >> count;

累加总和的循环语句进行了缩进,以显示这是for循环的一部分:

for (int i=1; i <=count; i++)
sum +=i;

因为循环语句只有一条,所以没有使用花括号。这个循环的结果是在变量sum中累加从1到count的整数。循环中的执行顺序如下所示:

(1)执行第一个表达式。此表达式声明整型变量i,并把它初始化为1。

(2)执行第二个表达式,检查i是否小于或等于count。如果i <=count为true,就进入第(3)步。如果它等于false,就进入第(6)步。

(3)执行循环语句,把i的当前值加到sum上。

(4)执行第三个表达式,递增i的值。

(5)返回到第(2)步。

(6)退出循环。

该程序将i的值连续加到sum中,从1开始,直到count的值为止。最后,当i递增到count+1时,for循环结束。程序接着执行循环体后面的语句,即输出在sum中累加的总和。

在for循环的初始化表达式中声明变量是合法的,这种用法很普遍。