C++PrimerPlus 第六章 分支语句和逻辑运算符 - 6.2 逻辑表达式
6.2 逻辑表达式
经常需要测试多种条件。例如,字符要是小写,其值就必须大于或等于 ‘a’,且小于或等于 ‘z’。如果要求用户使用y或n进行响应,则希望用户无论输入大写(Y和N)或小写都可以。为满足这种需要,C++提供了3种逻辑运算符,来组合或修改已有的表达式。这些运算符分别是逻辑OR(||)、逻辑AND(&&)和逻辑NOT(!)。下面介绍这些运算符。
6.2.1 逻辑OR运算符:||
在英语中,当两个条件中有一个或全部满足某个要求时,可以用单词or来指明这种情况。例如,如果您或您在配偶在MegaMicro公司工作,您就可以参加MegaMicro公司的野餐会。C++可以采用逻辑OR运算符(||),将两个表达式组合在一起。如果原来表达式中的任何一个或全部都为true(或非零),则得到的表达式的值为true;否则,表达式的值为false。下面是一些例子:
5 == 5 || 5 == 9 //true because first expression is true
5 > 3 || 5 > 10 //true because first expression is true
5 > 8 || 5 < 10 //true because second expression is true
5 < 8 || 5 > 2 //true because both expressions are true
5 > 8 || 5 < 2 //false because both expressions are false
由于||的优先级比关系运算符低,因此不需要在这些表达式中使用括号。下表总结了||的工作原理。
C++规定,||运算符是个顺序点(sequence point)。也是说,先修改左侧的值,再对右侧的值进行判定(C++11的说法是,运算符左边的子表达式先于右边的子表达式)。例如,请看下面的表达式。
i++ < 6 || i == j
假设i原来的值为10,则在对i和j进行比较时,i的值将为11。另外,如果左侧的表达式为true,则C++将不会去判定右侧的表达式,因为只要一个表达式为true,则整个逻辑表达式为true(读者可能还记得,冒号和逗号运算符也是顺序点)。
程序清单6.4在一条if语句中使用||运算符来检查某个字符的大写或小写。另外,它还使用了C++字符串的拼接特性(参见第4章)将一个字符串分布在3行中。
程序清单6.4 or.cpp
//or.cpp -- using the logical OR operator
#include<iostream>
int main()
{
using namespace std;
cout << "This program may reformat your hard disk\n"
"and destory all your data.\n"
"Do you wish to continue?<y/n>";
char ch;
cin >> ch;
if (ch == 'y' || ch == 'Y')
cout << "You were warned!\a\a\n";
else if (ch == 'n' || ch == 'N')
cout << "A wise choice ... bye\n";
else
cout << "That wasn't a y or n! Apperantly you "
"can't follow\ninstructions, so "
"I'll trash your disk anyway.\a\a\a\n";
return 0;
}
该程序不会带来任何威胁,下面是其运行情况:
This program may reformat your hard disk
and destory all your data.
Do you wish to continue?<y/n>N
A wise choice ... bye
由于程序只读取一个字符,因此只读取响应的第一个字符。这意味着用户可以用NO!(而不是N)进行回答,程序将只读取N。然而,如果程序后面再读取输入时,将从O开始读取。
6.2.2 逻辑AND运算符:&&
逻辑AND运算符(&&),也是将两个两个表达式组合成一个表达式。仅当原来的两个表达式都为true时,得到的表达式的值才为true。下面是一些例子:
5 == 5 && 4 == 4 //true because both expressions are true
5 == 3 && 4 == 4 //false because first expression is false
5 > 3 && 5 > 10 //false because second expression is false
5 > 8 && 5 < 10 //false because first expression is false
5 < 8 && 5 > 2 //true because both expressions are true
5 > 8 && 5 < 2 //false because both expressions are false
由于&&的优先级低于关系运算符,因此不必在这些表达式中使用括号。和||运算符一样,&&运算符也是顺序点,因此将首先判定左侧,并且在右侧被判定之前产生所有的副作用。如果左侧为false,则整个逻辑表达式必定为false,在这种情况下,C++将不会再对右侧进行判定。下表总结了&&运算符的工作方式。
程序清单6.5演示了如何用&&来处理一种常见的情况——由于两种不同的原因而结束while循环。在这个程序清单中,一个while循环将值读入到数组。一个测试(j < ArSize)在数组被填满时循环结束,另一个测试(temp >= 0)让用户通过输入一个负值来提前结束循环。该程序使用&&运算符将两个测试组合成一个条件。该程序还使用了两条if语句、一条if else语句和一个for循环,因此它演示了本章和第5章的多个主题。
程序清单6.5 and.cpp
//and.cpp -- using the logical AND operator
#include<iostream>
const int ArSize = 6;
int main()
{
using namespace std;
float naaq[ArSize];
cout << "Enter the NAAQs (New Age Awareness Quotients) "
<< "of\nyour neighbors/ Program terminates "
<< "when you make\n" << ArSize << " entries "
<< "or enter a negative value.\n";
int i = 0;
float temp;
cout << "First value: ";
cin >> temp;
while (i < ArSize && temp >= 0) //2 quitting criteria
{
naaq[i] = temp;
++i;
if (i < ArSize) //room left in the array,
{
cout << "Next value: ";
cin >> temp; //so get next value
}
}
if (i == 0)
cout << "No data -- bye\n";
else
{
cout << "Enter your NAAQ: ";
float you;
cin >> you;
int count = 0;
for (int j = 0; j < i; j++)
if (naaq[j] > you)
++count;
cout << count;
cout << " of your neighbors have greater awareness of\n"
<< "the New Age than you do.\n";
}
return 0;
}
注意,该程序将输入放在临时变量temp中。在核实输入有效后,程序才将这个值赋给数组。
下面是该程序的两次运行情况。一次在输入6个值后结束:
Enter the NAAQs (New Age Awareness Quotients) of
your neighbors/ Program terminates when you make
6 entries or enter a negative value.
First value: 28
Next value: 72
Next value: 15
Next value: 6
Next value: 130
Next value: 145
Enter your NAAQ: 50
3 of your neighbors have greater awareness of
the New Age than you do.
另一次在输入负值后结束:
Enter the NAAQs (New Age Awareness Quotients) of
your neighbors/ Program terminates when you make
6 entries or enter a negative value.
First value: 123
Next value: 119
Next value: 4
Next value: 89
Next value: -1
Enter your NAAQ: 123.031
0 of your neighbors have greater awareness of
the New Age than you do.
6.2.3 用&&来设置取值范围
&&运算符还允许建立一系列if else if else语句,其中每种选择都对应于一个特定的取值范围。程序清单6.6演示了这种方法。另外,它还演示了一种用于处理一系列消息的技术。与char指针变量可以通过指向一个字符串的开始位置来标识该字符串一样,char指针数组也可以标识一系列字符串,只要将每一个字符串的地址赋给各个数组元素即可。程序清单6.6使用qualify数组来存储4个字符串的地址,例如,qualify[1]存储字符串“mud tug-of-war\n”的地址。然后,程序便能够将cout、strlen()或strcmp()用于qualify[1],就像用于其他字符串指针一样。使用const限定符可以避免无意间修改这些字符串。
程序清单6.6 more_and.cpp
//more_and.cpp -- using the logical AND operator
#include<iostream>
const char* qualify[4] = //an array of pointers
{ //to strings
"10,000-meter race,\n",
"mud tug-of-war.\n",
"masters canie jousting.\n",
"pie-throwing festival.\n"
};
int main()
{
using namespace std;
int age;
cout << "Enter your age in years: ";
cin >> age;
int index;
if (age > 17 && age < 35)
index = 0;
else if (age >= 35 && age < 50)
index = 1;
else if (age >= 50 && age < 65)
index = 2;
else
index = 3;
cout << "You qualify for the " << qualify[index];
return 0;
}
下面是该程序的运行情况:
Enter your age in years: 87
You qualify for the pie-throwing festival.
由于输入的年龄不与任何测试取值范围匹配,因此程序将索引设置为3,然后打印相应的字符串。
6.2.4 逻辑NOT运算符:!
!运算符将它后面的表达式的真值取反。也是说,如果expression为true,则!expression是false;如果expression为false,则!expression是true。更准确地说,如果expression为true或非零,则!expression为false。
通常,不使用这个运算符可以更清楚地表示关系:
if (!(x > 5)) //if (x <= 5) is clearer
然而,!运算符对于返回true-false值或可以被解释为true-false值的函数来说很有用。例如,如果C-风格字符串s1和s2不同,则strcmp(s1, s2)将返回非零(true)值,否则返回0。这意味着如果这两个字符串相同,则!strcmp(s1, s2)为true。
程序清单6.7使用这种技术(将!运算符用于函数返回值)来筛选可赋给int变量的数字输入。如果用户定义的函数is_int()(稍后将详细介绍)的参数位于int类型的取值范围内,则它将返回true。然后,程序使用while(!is_int(num))测试来拒绝不在该取值范围内的值。
程序清单6.7 not.cpp
//not.cpp -- using the not operator
#include<iostream>
#include<climits>
bool is_int(double);
int main()
{
using namespace std;
double num;
cout << "Yo, dude! Enter an integer value: ";
cin >> num;
while (!is_int(num)) //continue while num is not int-able
{
cout << "Out of range -- please try again: ";
cin >> num;
}
int val = int(num); //type cast
cout << "You've entered the integer " << val << "\nBye\n";
return 0;
}
bool is_int(double x)
{
if (x <= INT_MAX && x >= INT_MIN) //use climits values
return true;
else
return false;
}
下面是该程序在int占32位的系统上的运行情况:
Yo, dude! Enter an integer value: 6234128679
Out of range -- please try again: -8000222333
Out of range -- please try again: 99999
You've entered the integer 99999
Bye
6.2.5 逻辑运算符细节
正如本章前面指出的,C++逻辑OR和逻辑AND运算符的优先级都低于关系运算符。这意味着下面的表达式
x > 5 && x < 10
将被解释为:
(x > 5) && (x < 10)
另一方面,!运算符的优先级高于所有的关系运算符和算术运算符。因此,要对表达式求反,必须用括号将其括起,如下所示:
!(x > 5) //is it false that x is greater than 5
!x > 5 //is !x greater than 5
第二个表达式总是为false,因为!x的值只能为true或false,而它们将被转换为1或0。
逻辑AND运算符的优先级高于逻辑OR运算符。因此,表达式:
age > 30 && age < 45 || weight > 300
被解释为:
(age > 30 && age < 45) || weight > 300
也是说,一个条件是age位于31~44,另一个条件是weight大于300。如果这两个条件中的一个或全部都为true,则整个表达式为true。
当然,还可以用括号将所希望的解释告诉程序。例如,假设要用&&将age大于50或weight大于300的条件与donation大于1000的条件组合在一起,则必须使用括号将OR部分括起:
(age > 50 || weight > 300) && donation > 1000
否则,编译器将把weight条件与donation条件(而不是age条件)组合在一起。
虽然C++运算符的优先级规则常可能不使用括号便可以编写复合比较的语句,但最简单的方法还是用括号将测试进行分组,而不管是否需要括号。这样代码容易阅读,避免读者查看不常使用的优先级规则,并减少由于没有准确记住所使用的规则而出错的可能性。
C++确保程序从左向右进行计算逻辑表达式,并在知道答案后立刻停止。假如,假设有下面的条件:
x != 0 && 1.0 / x > 100.0
如果第一个条件为false,则整个表达式肯定为false。这是因为要使整个表达式为true,每个条件都必须为true。知道第一个条件为false后,程序将不判定为第二个条件。这个例子非常幸运,因为计算第二个条件将导致被0除,这是计算机没有定义的操作。
6.2.6 其他表示方式
并不是所有的键盘都提供了用作逻辑运算符的符号,因此C++标准提供了另一种表示方式,如下表所示。标识符and、or和not都是C++保留字,这意味着不能将它们用作变量名等。它们不是关键字,因为它们都是已有语言特性的另一种表示方式。另外,它们并不是C语言中的保留字,但C语言程序可以将它们用作运算符,只要在程序中包含了头文件iso646.h。C++不要求使用头文件。