前面只介绍了courter<=10、total>1000和number!=sentinel Value之类的简单条件(simplecondition)。我们用关系运算符>、<、>=、<=和相等运算符==、!=表示这些条件。每个判断只测试一个条件。要在每个判断中测试多个条件,可以在不同语句中或嵌套if(if/else)结构中进行这些测试。
C++提供的逻辑运算符(logical operator)可以用简单条件组合成复杂条件。逻辑运算符有逻辑与( &&)、逻辑或(||)和逻辑非(!),下面将举例说明。
假设要保证两个条件均为true之后再选择某个执行路径,这时可以用 &&逻辑运算符:
if { gender == 1&& age>= 65 ) ++senior Females;
这个if语句包含两个简单条件。条件gender==1确定这个人是男是女,条件age>=65确定这个人是否为老年人。&&逻辑运算符左边的简单条件先求值,因为::的优先级高于&&。如果需要,再对&&逻辑运算符右边的简单条件求值,因为>=的优先级高级于&&(稍后将会介绍,&&逻辑运算符右边的条件只在左边为true时才求值)。然后if语句考虑下列组合条件:
gender==l &&age>=65
如果两边的简单条件均为true,则这个条件为true。最后.如果这个条件为true,则seniorFemales递增1。如果两边的简单条件有一个为false,则程序跳过该递增,处理if后面的语句。上述组合条件可以通过增加多余的括号而变得更加清楚:
(gender==1) &&(age>=65)
尽管3<x<7条件在数学上是正确的,但在C++中无法正确求值,应改成(3<x&&x<7)。
图2.28的表格总结了 &&逻辑运算符。表中显示了表达式1和表达式2的四种false和true值组合,这种表称为真值表(truth table)。C++对所有包括逻辑运算符、相等运算符和关系运算符的所有表达式求值为false或true。
表达式1 表达式2 表达式1&&表达式2 false false false false true false true false false true true true
为了与旧版C++标准兼容,bool值为true表示任何非0值,bool值为false表示0值。
下面考虑逻辑或运算符(||)。假设要保证两个条件至少有一个为true之后再选择某个执行路径,这时可以用逻辑或运算符,如下列程序段所示:
if(semesterAverage)>=90 ||finalExam>=90) cout<<"Student grade is A"<<endl;
上述条件也包含两个简单条件。条件semesterAverage>=90确定学生整个学期的表现是否为"A",条件finalExam>=90确定该生期末考试成绩是否优秀。然后if语句考虑下列组合条件:
semesterAverage>=90 || finalExam>=90
如果两个简单条件至少有一个为true,则该生评为“A”。注意只有在两个简单条件均为false时才不打印消息"Student grade is A"。图2.29是逻辑或(||)运算符的真值表。
&&逻辑运算符的优先级高于||运算符。这两个运算符都是从左向右结合。包含&&和||运算符的表达式只在知道真假值时才求值。
这样,求值下列表达式:
gender==1 && age>=65
将在gender不等于1时立即停止(整个表达式为false),而gender等于1时则继续求值(整个表达式在age>=65为true时为true。
表达式1 表达式2 表达式1&&表达式2 false false false false true true true false true true true true
在使用&&逻辑运算符的表达式中,一个条件(称为相关备件)要在另一个条件为true时才有求值的必要。这时,相关条件应放在另一条件的后面,否则可能发生错误。
在使用&&逻辑运算符的表达式中,如果两个条件是相互独立的,则应把false可能性较大的条件放在左边;在使用||逻辑运算符的表达式中,应把true可能性较大的条件放在左边。这样可以减少程序执行时间。
C++提供了逻辑非(!)运算符,使程序员可以逆转条件的意义。与&&和||运算符不同的是,&&和||运算符组合两个条件(是二元运算符),而逻辑非(!)运算符只有一个操作数(是一元运算符)。逻辑非(!)运算符放在条件之前,在原条件(不带逻辑非运算符的条件)为false时选择执行路径,例如:
if(grade!=sentinelValue) cout<<"The next grade is"<<grade<<endl;
这种灵活性有助于程序员以更自然或更方便的方式表达条件。
图2.31显示了前面介绍的c++运算符的优先级和结合律。运算符优先级从上到下逐渐降低。
运算符 结合律 类型 () 从左向右 括号 ++ -- + - ! static_cast() 从右向左 一元 * / % 从左向右 乘 + - 从左向右 加 << >> 从左向右 插入/读取 < <= > >= 从左向右 关系 == != 从左向右 相等 && 从左向右 逻辑与 || 从左向右 逻辑或 ?: 从右向左 条件 = += -= *= /= %= 从右向左 赋值 , 从左向右 逗号 图 2.31 运算符优先级和结合律
这是C++程序员常见的错误,包括熟练的C++程序员也会把相等(==)与赋值(=)运算符相 混淆。这种错误的破坏性在于它们通常不会导致语法错误,而是能够顺利编译,程序运行完后,因为运行时的逻辑错误而得到错误结果。
C++有两个方面会导致这些问题。一个是任何产生数值的表达式都可以用于任何控制结构的判断部分。如果数值为0,则当作false,如果数值为非0,则当作true。第二是C++赋值会产生一个值,即赋值运算符左边变量所取得的值。例如,假设把下列代码:
if(payCode==4) cout<<"You get a bonus!" << endl;
误写成如下形式:
if(payCode=4) cout << "You get a bonus!" << endl;
第一个if语句对paycode等于4的人发奖金。而第二个if语句则求值if条件中的赋值表达式为常量4。由于非0值解释为true,因此这个if语句的条件总是true,则人人都获得一份奖金,不管其paycode为多少。更糟的是,本来只要检查paycode,却已经修改了Poycode。
用==运算符进行赋值或用=运算符表示相等是个逻辑错误。
程序员通常将条件写成x==7,即变量名在左边,常量在右边。如果反过来.即变量名在右边,常量在左边,写成7==x,刚编译器能防止程序员把==运算符写成=。编译器把这个错误当作语法错误,因为赋值语句左边只能放变量名.这样至少可以防止运行时逻辑错误的潜在破坏。
变量名可称为左值(lvalue),因为它可以放在赋值运算符左边。常量称为右值(rvalue),因为它只能放在赋值运算符右边。注意,左值可以作为右值,但右值不能用作左值。
还有一个问题也同样麻烦。假设程序员要用下列简单涪句给一个变量赋值:
x = 1;
但却写成:
x == 1;
这也不是语法错误,编译器只是求值条件表达式。如果x等于1,则条件为true,表达式返回true值。
如果x不等于1,则条件为false,表达式返回false值。不管返回什么值都没有赋值运算符,因此这个值丢失,x值保持不变,有可能造成执行时的逻辑错误。但这个问题没有简单的解决办法。