3.4 布尔类型
在日常生活中,我们除了需要使用int类型的变量表示216路公交车;需要使用float类型的变量表示西红柿3.5元一斤,有时候还需要表示一种数据,那就是逻辑状态:
“这次的C++考试你过了没有?”
“他到底爱不爱我?”
这里的“过了没有”以及“爱不爱”都是表示一种逻辑判断。与之前我们用数值数据类型表示的公交车线路以及西红柿价格可以有多种取值不同,这种逻辑判断状态具有“非此即彼”的特殊性。对于考试状态,要么是“过了”,要么是“没过”,二者必选其一。正因为如此,在C++中,我们同样也使用一种特殊的数据类型——布尔类型——来表达这种“非此即彼”的逻辑判断状态。布尔类型的变量只可以被赋予值true或者false,分别表示逻辑的真与假、是与非。布尔类型的说明符是“bool”。C++标准并没有指定布尔类型数据的长度,在Visual C++中布尔类型占1个字节。例如:
// 布尔类型变量bPass,表示考试是否通过 // 赋值为true,表示考试通过 bool bPass = true;
与int等数值类型数据主要用于计算不同,布尔类型的数据主要用来保存逻辑判断的结果,或者是用于条件结构或者循环结构(在稍后的第4章将详细介绍)中,对程序的执行流程进行控制。例如:
cout<<"请输入你的分数:"<<endl; // 保存输入分数的int类型变量 int nScore = 0; // 输入分数 cin>>nScore; // 保存考试通过与否的bool类型变量 // 默认状态为false,表示没有通过 bool bPass = false; // 用条件结构进行逻辑判断 // 判断输入的分数是否大于等于60 if(nScore >= 60) { // 保存逻辑判断的结果 // 如果输入的分数大于等于60,则赋值为true, // 表示考试通过,否则继续保留其初始值false,表示没有通过 bPass = true; } // 在条件结构中,根据bPass的取值不同, // 对程序的执行路径进行控制 if(bPass) { // 如果bPass的值为true,输出考试通过 cout<<"恭喜,你通过了考试"<<endl; } else { // 如果bPass的值为false,输出考试未通过 cout<<"很遗憾,你没有通过考试"<<endl; }
在这段代码中,bPass这个bool类型的变量,首先用于保存逻辑判断的结果,记录了输入的分数nScore是否大于等于60的逻辑判断结果。如果nScore大于等于60,bPass就被赋值为true,否则,继续保留它的初始值false,表示nScore没有大于等于60。这样,bPass这个bool类型的变量就保存了“nScore是否大于等于60”这个逻辑判断的结果。
用bool类型变量保存逻辑判断的结果,不仅仅是为了保存,更多的还是为了用在条件或者循环结构中对程序的执行流程进行控制。在其后的if条件结构中,bPass又被用作了条件判断的依据,如果bPass的值为true,则输出考试通过的提示,否则,就输出考试未通过的提示。这样,根据bPass这个bool类型变量的取值不同,我们的程序就有了不同的执行路径。
最佳实践:杜绝bool类型和整型的隐式转换
虽然bool类型变量只有true和false这两个逻辑值,但当其使用在一个需要数值算术值的地方时,bool类型变量的值将被隐式地转换成为一个整型数值。如果bool类型变量的值是false,则转换后参与运算的值就是0;反之就是1。反过来,如果我们把一个数值(整数或小数)赋值给一个bool类型变量,那么也会发生从数值数据到bool类型数值的隐式转换。数值0会被转换为为false,而其他任何非0的数值会被转换为true。例如:
bool a = 4; // 4被转换为true,a的值为true int b = a; // a被转换为1,b的值为1 int c = a + b; // a被转换为1参与运算,c的值为2
这种转换发生得非常隐蔽,某些编译器甚至连警告信息都不会给出。而一旦代码的行为非常隐蔽,这就意味着其中很可能会隐藏着一些不容易被察觉的错误。所以我们应当尽量避免用数值为bool类型的变量赋值,或者是将bool类型的变量用于算术运算,杜绝这种“偷偷摸摸”的小动作的发生。
3.5 字符与字符串类型
“你的车牌号是多少?”
“陕A-82103”
我们知道,程序设计语言本质上是用来抽象、描述和表达现实世界的。面对现实世界中的各种数值数据(比如,表示公交车路线的216路,表示西红柿价格的3.5元一斤),我们可以用之前所介绍的数值数据类型定义变量(int nNo,float fPrice)来抽象和表达。除此之外,现实世界中还有另外一类数据——文字数据,比如上面例子中的车牌号“陕A-82103”就是一个文字数据。为了表达这一类文字数据,C++提供了字符类型和字符串类型两种数据类型专门用于抽象和表达文字数据。其中,字符类型用于表达单个的字符,比如‘A’、‘0’等等,而多个字符串联起来形成的字符串,我们则使用字符串类型来表达。
3.5.1 字符类型
在学习英语的时候,我们总是从ABC单个字母开始学习的,然后才能串联起来形成一句完整的话。而要想在C++中用字符串表达一个比较复杂的文字数据,我们同样也得从构成字符串的单个字符开始学起。在C++中,我们用字符类型来抽象和表达单个的常见字符,其类型说明符是“char”。例如:
// 定义字符类型变量cA,并用字符常量‘A’对其进行赋值 char cA = 'A'; // cA变量表示大写字母字符‘A’ char cB; cB = '-'; // cB变量表示符号字符‘-’ // 输出字符‘A’ cout<<cA<<endl;
虽然字符类型的变量总是以字符的面目出现(赋值和输出),但在本质上,它其实可以看作是一种占用内存空间更少取值范围更小的整型数据类型。它只占用1 个字节的内存空间,相应的其取值范围也缩小为-128~127。当我们需要表达一个取值比较小的整数时,为了节省内存资源,我们可以使用char类型来表达。跟整型数据类型一样,字符类型也可以受到signed/unsigned关键字的修饰,形成有/无符号的字符类型。因此,char类型也可以参与算术运算。例如:
// 将大写字母字符‘A’对应的ASCII码值赋值给字符类型变量c // 等价于char c = ‘A’; char c = 65; // 变量t表示不可见的控制字符‘\t’,表示输出一个Tab控制 char t = '\t'; // 循环输出26个大写字母字符 for(int i = 0; i < 26; ++i) { // 输出c所代表的字符 cout<<c<<t; // 字符类型变量加1,使其成为ASCII表中的下一个字符 c = c + 1; }
知道更多:用wchar_t表示中文字符
因为char字符类型取值范围有限,只能用来表示有限的ANSCII字符表中的字符,包括常见的字母字符和控制字符等。而如果要表示更大范围内的字符,比如上面例子中的‘陕’这个中文字符,char就显得鞭长莫及了。为了弥补char类型的不足,C++提供了另外一种字符数据类型wchar_t,它占用2个字节的内存空间,取值范围更广,因而可以表示更大范围的字符,当然也包括中文字符了。例如,可以通过下面的方式来输出一个中文字符。不过这里需要注意的是,因为我们的代码中有扩展的中文字符,所以要求我们的代码文件使用UTF-8的编码格式进行保存。
// 定义一个wchar_t类型字符变量 // 并用一个中文字符对其赋值 wchar_t cChs = L'曾'; // 设置wcout输出对象的区域并输出中文字符 wcout.imbue ( locale ( "chs" ) ); wcout<<cChs<<endl;
这里值得提醒的是,字符常量或字符串常量前面的L前缀,表示对这里的字符或字符串采用wide-character字符集(宽字符集,通常是UNICODE字符集)对其进行编码。而如果不使用L前缀,则表示使用multibyte-character字符集(多字节字符集)对其进行编码。所以,如果我们要用某个字符或字符串常量对一个宽字符(wchar_t)或宽字符串(wstring)变量进行初始化或赋值时,我们应该在这个字符或字符串常量前加上L前缀。反之,如果是对char和string类型的变量进行初始化或赋值时,则不需要添加L前缀。