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

图4-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块后面的语句总是执行。
程序示例4.2—— 作出决策
下面试用if语句。创建一个程序,检查从键盘上输入的一个整数值:
//Program 4.2 Using an if statement
#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;
这个语句总是执行。
嵌套的if语句
在if语句中的条件为true时才执行的语句本身也可以是一个if语句,这种情况称为嵌套的if语句。只有外层if的条件为true时,才测试内层if的条件。嵌套在一个if语句中的if语句也可以包含另一个嵌套的if语句。一般情况下,可以像这样继续嵌套if语句,嵌套的次数也可以是任意多次。
程序示例4.3—— 使用嵌套的if语句
下面用一个工作示例来演示嵌套的if语句。该示例测试从键盘上输入的字符是否为字母。这个示例很好地使用了嵌套的if语句,但其中一些固有的假设最好避免,读者可以试着找出这些假设。下面是代码:
//Program 4.3 Using a nested if
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main() {
char letter=0; // Store input in here
cout << "Enter a letter: "; // Prompt for the input
cin >> letter; // then read a character
if(letter >= 'A') { // Test for 'A' or larger
if(letter <= 'Z') { // Test for 'Z' or smaller
cout << "You entered an uppercase letter."
<< endl;
return 0;
}
}
if(letter >= 'a') //Test for 'a' or larger
if(letter <= 'z') { //Test for 'z' or smaller
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') { //Test for 'A' or larger
if (letter <= 'Z') { //Test for 'Z' or smaller
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') //Test for 'a' or larger
if (letter <= 'z') { //Test for 'z' or smaller
cout << "You entered an lowercase letter."
<< endl;
return 0;
}
但如果仔细检查,就会注意到小写字符的测试只包含一对花括号,而大写字母的测试包含两对花括号。这里花括号中的代码块属于内层的if语句。实际上,这两种方式都是对的:在C++中,if(condition){…}是一个语句,不需要放在花括号中。同样,如果觉得使用较多的花括号能使代码更清晰,就可以使用它们。最后,与大写字母测试一样,这些代码隐含了小写字母编码的假设。
只有在输入的字符不是字母时,才执行最后一个if语句块后面的输出语句,它会显示一个消息,然后执行return语句。嵌套的if语句和输出语句之间的关系更容易理解,因为每个if语句块都进行了缩进。在C++中,缩进格式通常用于提供程序逻辑的可视化线索。
如本例开头所述,该程序演示了嵌套的if语句块如何工作,但这并不是测试字符的好方法。使用标准库可以编写出独立于字符编码的程序。下面就介绍这部分内容。
不依赖编码的字符处理
标准库提供了许多函数,它们可以在程序中执行许多任务。表4-2列出了这些函数。在程序中包含<cctype>头文件,就可以访问一组非常有用的函数集,来测试字符。在测试字符时,需要给函数传送一个int类型的变量或字面量。如果传送了char类型的值,编译器会把它自动转换为int。
表4-2 测试字符的函数
|
函 数 |
所执行的动作 |
|
isupper() |
测试大写字母A到Z |
|
islower() |
测试小写字母a到z |
|
isalpha() |
测试大写字母或小写字母 |
|
isdigit() |
测试数字0到9 |
|
isxdigit() |
测试十六进制数字0到9、a到f或A到F |
|
isalnum() |
测试字母或数字(例如字母表上的字符和数字) |
|
isspace() |
测试空白,空白可以是一个空格、换行符、回车符、换页符、水平制表符或垂直制表符 |
|
iscntrl() |
测试控制符 |
|
isprint() |
测试可打印的字符,即大写或小写字母、数字、标点符号或空格 |
|
isgraph() |
测试图形字符,即除了空格之外的所有可打印字符 |
|
ispunct() |
测试标点符号,即非字母或数字的所有可打印字符,标点符号可以是空格或下面的字符:_ { } [ ] # ( ) < > % : ; . ? * + - / ^ & | ~ ! = , \ " ' |
这些函数都返回一个int类型的值。如果字符的类型与要测试的类型相同,该值就为正(true),否则就为0(false)。为什么这些函数不返回bool类型的值?这看上去似乎更有意义。原因是包含这些函数的C标准库是在bool类型引入C++之前建立的。
<cctype>头文件还提供了两个函数,如表4-3所示,在大写字符和小写字符之间转换,传送给这两个函数的字符应是int类型,返回的结果为int类型:
表4-3 转换字符的函数
|
函 数 |
说 明 |
|
tolower() |
如果给函数传送了一个大写字母,就返回该字母的小写形式,否则就返回未修改的传送进来的字母 |
|
toupper() |
如果给函数传送了一个小写字母,就返回该字母的大写形式,否则就返回未修改的传送进来的字母 |
可以使用这些函数实现前面的例子,而无需字符编码的任何假设。不同环境中的不同字符编码总是由标准库函数来考虑,用户不需要考虑。因为使用标准库函数后,也不需要使用嵌套的if语句,所以代码要比前面简单得多。
注意所有这些字符测试函数,除isdigit()和isxdigit()之外,都在当前环境下测试变元。本地环境确定了本地数据是如何处理的。不同的国家使用不同的字符集表示字母,所以某个字符编码是否解释为字母取决于本地环境。货币单位和小数的显示方式也随着本地环境的不同而不同。调用在<clocale>头文件中声明的setlocale()函数可以设置本地环境。这个函数接受两个变元:第一个变元指定应用本地环境的函数的类别,第二个变元指定本地环境。用于第一个变元的值必须是在<clocale>头文件中声明的值,表4-4列出了这些值。
表4-4 受本地环境影响的类别值
|
值 |
说 明 |
|
LC_ALL |
指定所有的类别 |
|
LC_CTYPE |
指定字符处理 |
|
LC_COLLATE |
指定字符串比较中的比较顺序 |
|
LC_MONETARY |
指定货币信息的格式 |
|
LC_NUMERIC |
指定小数点字符 |
|
LC_TIME |
指定时间值的格式 |
setlocale()的第二个变元是一个指定本地环境的字符串。字符串"C"是默认值,对应于拉丁字母'A'到'Z'。可以用于指定其他本地环境的字符串集是由实现方式定义的,但通常包括简单明了的国家规范,如Germany。
C++头文件<locale>提供了许多扩展功能的声明,它们可以处理依赖本地环境的数据,在程序中需要支持多个本地环境时,就应使用它们。
程序示例4.4—— 使用标准库字符转换函数
在修改上一个例子,使用标准库函数时,还可以扩展程序的功能,试用转换函数:
//Program 4.4 Using standard library character testing and conversion
#include <iostream>
#include <cctype> // Character testing and conversion
using std::cin;
using std::cout;
using std::endl;
int main() {
char letter=0; // Store input in here
cout<<endl
<< "Enter a letter: "; // Prompt for the input
cin>> letter; // then read a character
cout<<endl;
if (std::isupper(letter)) { //Test for upper case 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)) { //Test for lower case 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++程序,所以应限定名称。





