关系与逻辑表达式
关系表达式
- 在C++中,关系运算符用于判断表达式之间的大小关系,对两边的值进行6种情况的判断:
- 大于
>
和大于等于>=
运算符 - 小于
<
和小于等于<=
运算符 - 相等
==
和不相等!=
运算符
- 大于
- 关系运算符中,表达式结果的类型可以是字符或者数字,但不可以是字符串
- 关系表达式返回一个布尔值:成立为
1(true)
代表真,不成立为0(false)
代表假
Tips:判断两个数是否相等,用的是==
而非=
,因为=
是用于赋值操作的。
- 浮点数一般不会用
==
或者!=
来判断,因为可能产生表示误差。 - 比较两个浮点数是否相等,需要看两个数的差值是否小于一定的精度,比如:
- 对于浮点数a和b,如果
(a - b) < 1e-6
如果值为真,就判断两个浮点数相等。
- 对于浮点数a和b,如果
举例:假设血量是整型数,小六希望判断己方血量hp_a
和敌方血量hp_b
两个数值之间的关系。
判断己方与敌方血量(整型数)关系的程序段
int hp_a, hp_b;
hp_a = 456;
hp_b = 857;
cout << "己方血量: " << hp_a << " 敌方血量: " << hp_b << endl;
// 关系运算符两边都是变量的基础用法
// 判断 自己血量 是否大于 敌方血量
cout << (hp_a > hp_b) << endl;
// 判断 自己血量 是否大于等于 敌方血量
cout << (hp_a >= hp_b) << endl;
// 判断 自己血量 是否小于 敌方血量
cout << (hp_a < hp_b) << endl;
// 判断 自己血量 是否小于等于 敌方血量
cout << (hp_a <= hp_b) << endl;
// 判断 自己血量 是否等于 敌方血量
cout << (hp_a == hp_b) << endl;
// 判断 自己血量 是否不等于 敌方血量
cout << (hp_a != hp_b) << endl;
// 关系运算符一边是表达式,一边是变量
// 判断 自己回50点血后的血量 是否小于 敌方血量
cout << ((hp_a + 50) < hp_b) << endl;
// 关系运算符一边是变量,一边是数字
// 判断 自己血量 是否小于 100
cout << (hp_a < 100) << endl;
举例:假设血量是浮点数,小六希望判断己方血量 hp_c
和 敌方血量hp_d
是否相等。
判断己方与敌方血量(浮点数)关系的程序段
float hp_c, hp_d;
hp_c = 100.0;
hp_d = 100.0;
cout << ((hp_c - hp_d) < 1e-6) << endl;
逻辑表达式
- 关系表达式可以判断某个条件是否成立,逻辑表达式中包含了多个关系表达式,可以对多个条件进行判断,例如:
- 多个条件是否同时成立
- 多个条件中是否至少有一个成立
- 某个或某些条件是否不成立
- C++使用逻辑运算符连接多个关系表达式,从而构建逻辑表达式。
- 常用的逻辑运算符有以下三种:
- 逻辑
AND
运算符&&
:判断两个关系表达式是否同时成立 - 逻辑
OR
运算符||
:判断两个关系表达式是否至少有一个成立 - 逻辑
NOT
运算符!
:判断某个关系表达式是否不成立C++使用逻辑运算符连接多个关系表达式,从而构建逻辑表达式。
- 逻辑
- 假设
p1
和p2
是关系表达式的返回结果,逻辑运算符的运算规则可以参考下表:
p1 | p2 | p1&&p2 | p1llp2 | !p1 |
---|---|---|---|---|
0 | 0 | 0 | 0 | 1 |
0 | 1 | 0 | 1 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 1 | 1 | 1 | 0 |
Tips:判断多个条件是否成立时,我们可以组合逻辑运算符,构成更复杂的逻辑表达式。
举例:判断当变量 a
是否是30以下或者100以上的偶数时,可以通过 (a%2==0) && ((a < 30) || (a > 100))
得到结果。
举例:小六最近在游戏上进步了一点点,他知道有的时候撤不撤退,不只需要看自己血量是否比敌方高,还需要看自己的蓝量是否比敌方高。
假设布尔值 higher_hp
表示自己血量比对方高,higher_mp
表示自己蓝量比对方高。
小六想要知道:“是否自己血量和蓝量都比敌方高”,“是否自己血量和蓝量和中至少有一种比敌方高”,“是否血量没有敌方高”,“是否自己血量和蓝量都没有敌方高”,你能够把这些信息都输出给小六吗?
判断敌我双方血量和蓝量的程序段
// 假设小六血量比敌方高,但是蓝量没有敌方高
bool higher_hp = 1;
bool higher_mp = 0;
// 是否自己血量和蓝量都比敌方高
cout << (higher_hp && higher_mp) << endl;
// 是否自己血量和蓝量和中至少有一种比敌方高
cout << (higher_hp || higher_mp) << endl;
// 是否血量没有敌方高
cout << (!higher_hp) << endl;
// 是否自己血量和蓝量都没有敌方高
cout << (!higher_hp && !higher_mp) << endl;
运算符优先级
- 至此,我们已经学习的运算符如下:
- 算术运算符(
+ - * / % ++ --
等) - 赋值运算符(
=
等) - 位运算符(
<< >>
等) - 关系运算符(
> >= < <= == !=
等) - 逻辑运算符(
&& || !
等)
- 算术运算符(
- 对于这些常用的运算符,按照计算优先级从高到低(左边更高,右边更低)进行排列,可以得到如下表格:
() | ! -(负号) ++ -- | * / % | + -(减号) | << >> | == != | && | ll |
---|
Tips:对于一个表达式,虽然可以通过计算优先级省略掉括号,但为了代码的可读性,我们在编写时一般都选择把括号加上。
习题
举例:生日在2月29日的程序员小键希望用一段代码判断输入的某一年是否为闰年。
- 判断“闰年”的口诀是:四年一闰,百年不闰,四百年又闰。即闰年分为两种:
- 一种是被400整除的年份
- 一种是不被100整除,但是被4整除的年份
为了综合这两个条件,小键通过逻辑表达式来实现了这个功能:
判断闰年的程序段(一)
int year;
cin >> year;
bool is_leap_year;
// 是400的倍数;或者是4的倍数并且不是100的倍数
is_leap_year = (year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0));
cout << is_leap_year << endl;
- 在C++中,对于逻辑运算符
&&
、||
、!
来说:- 参与计算的条件返回值为0,就会被当作假
- 如果
year = 2000
是400的倍数,year % 400
的结果为0,此时(year % 400
)会被当作假
- 如果
- 参与计算的条件返回值非0,就会被当作真
- 如果
year = 2001
不是400的倍数,year % 400
结果为1,此时(year % 400
)会被当作真
- 如果
- 参与计算的条件返回值为0,就会被当作假
通过这个特性,用逻辑表达式判断闰年也可以这么写:
判断闰年的程序段(二)
int year;
cin >> year;
bool is_leap_year;
is_leap_year = !(year % 400) || (!(year % 4) && (year % 100));
cout << is_leap_year << endl;
if 语句
if 基础用法
if
基础用法:控制程序在指定条件下执行指定语句。- 语法:
if (成立条件表达式) {
当条件成立时需要执行的语句
}
- 语法解析:
- 成立条件表达式:用圆括号包裹,一般是关系表达式或者逻辑表达式,比如:
a<b
,a<b && b<c
- 如果条件成立(成立条件表达式的值为“1或True”),则执行语句;
- 执行语句可以是一行代码,也可以是多行代码
- 如果执行语句是多行代码,需要用大括号包裹;
- 执行语句相比其他语句向右移动一些,就是缩进。读者可以一目了然的知道哪些语句是当条件成立时执行的,可读性更强。
- 成立条件表达式:用圆括号包裹,一般是关系表达式或者逻辑表达式,比如:
举例:
小键希望用一段代码提醒小戴在下雨时带上伞。
假设天气预报上的降水概率大于40%就表示要下雨,小键需要写一段代码,在输入降水概率后,判断如果要下雨,就输出“请带上伞”。
// 输入一个double类型的变量表示降水概率
double prob_of_precipitation;
cin >> prob_of_precipitation;
// 在降水概率大于40%这个条件成立时,需要执行的语句是:输出“请带上伞”。
if (prob_of_precipitation > 0.4)
cout << "请带上伞" << endl;
如果降雨条件成立时,要首先输出降水概率,并且输出“请带上伞”,那么小键就需要在执行语句外加上大括号:
double prob_of_precipitation;
cin >> prob_of_precipitation;
if (prob_of_precipitation > 0.4) {
cout << "降水概率:" << prob_of_precipitation << endl;
cout << "请带上伞" << endl;
}
if … else 用法
if ... else
用法:用来控制程序在指定条件下做事情A,否则做另外去做事情B。- 语法:
if (成立条件表达式) {
当条件成立时需要执行的语句
} else {
当条件不成立时需要执行的语句
}
- 举例:
我们都知道未成年人不能进入网吧,网吧老板需要小键帮忙写段代码,根据输入的年龄判断是否可以进入网吧。
如果年龄大于等于18,就输出“允许进入”;否则输出“禁止进入”。
// 先输入一个整型表示年龄
int age;
cin >> age;
// 判断年龄是否大于等于18,成立的话就输出“允许进入”;不成立就输出“禁止进入”。
if (age >= 18)
cout << "允许进入" << endl;
else
cout << "禁止进入" << endl;
if… else if 用法
if ... else if
用法:有些时候,我们会遇到大于两个分支的复杂情况。这时候,我们需要用到if ... else if
语句,保证在多种情况下,不同条件成立时可以做不同的事情- 语法:
if (成立条件表达式1) {
当条件1成立时需要执行的语句
} else if (成立条件表达式2) {
条件1不成立但是条件2成立时需要执行的语句
} else {
当条件1和2都不成立时需要执行的语句
}
- 语法执行流程:
- 在条件1成立时进行一些操作;
- 否则看条件2是否成立;
- 如果条件2成立,做一些操作;
- 否则做条件1和2都不成立时需要的操作。
- 注意点:
- 成立条件表达式:关系或者逻辑表达式;
- 最后的一个else:可以没有,你可以选择在条件1和条件2都不成立时,不做任何事情。
- 举例:
看见小键帮网吧老板的忙,公园售票处也想让小键帮忙写个程序,根据输入的年龄,输出对应的买票政策:如果是老人,就免票;小孩,就半票;否则就全票。
如果我们用年龄对人群做一个划分,假设年龄小于12岁,我们叫小孩;大于等于60岁,我们称之为老人。那么输入年龄,公园售票处该怎么表达对应的操作呢?
// 我们先读入年龄
int age;
cin >> age;
// 先判断是否是老人,是的话直接免票
// 如果不是老人,再判断是否是小孩,是的话半票
// 都不是的话,就全票
if (age >= 60)
cout << "免票" << endl;
else if (age < 12)
cout << "半票" << endl;
else
cout << "全票" << endl;
if… else if 的延伸用法
if ... else if
延伸用法:如果情况需要分成不只三种,我们也可以在else后面再接if
、else
、if
、else
,依次判断每个条件是否成立,成立就执行对应的操作,否则就判断下一个条件是否成立:
if (成立条件表达式1) {
当条件1成立时需要执行的语句
} else if (成立条件表达式2) {
否则,当条件2成立时需要执行的语句
} else if (成立条件表达式3) {
否则,当条件3成立时需要执行的语句
} ... else if (成立条件表达式n) {
否则,当条件n成立时需要执行的语句
} else {
当上述所有条件都不成立时需要执行的语句
}
if语句嵌套
if
语句嵌套:对于条件成立或者不成立的情况下需要执行的语句里,再嵌入if语句表示新的分支。
Tips:很多时候分支的情况会非常的复杂:当某个条件成立或者不成立时,还需要考虑另一个条件是否成立,根据另一个条件的成立与否再进行分支。这时候,就需要用到if语句的嵌套。
- 用法:
- 对于最外层的分支,每个分支需要执行的语句都可以再嵌入分支
- 嵌入的这个分支可以以任何形式出现,可以是if,可以是
if ... else
,也可以是if ... else if
举例:假设在条件1成立时,我们要通过条件2再进行分支,就可以这样写:
if (成立条件表达式1) {
if (成立条件表达式2) {
当条件1和2都成立时需要执行的语句
}
} else {
当条件1不成立时需要执行的语句
}
- 注意点1:如果省略大括号,
else
默认会匹配到最近的那一个if。- 举例:如果不加上大括号的话,下面程序会把最后的
else
和第二个if
匹配起来,表达的就不是我们之前说的意思了。
if (成立条件表达式1) if (成立条件表达式2) { 当条件1和2都成立时需要执行的语句 } else { 当条件1不成立时需要执行的语句 }
- 举例:如果不加上大括号的话,下面程序会把最后的
- 注意点2:if语句的嵌套会出现很多的大括号和缩进,大家想想如果有4层以上的嵌套会是什么样的情景?所以为了代码的可读性,尽量避免非必要的多重if语句嵌套。
switch语句
比如聊天机器人需要对不同的情况做出不同的反应,这显然也是一个分支结构。所以我们可以用if
语句完成:
// 输入一个字符代表自己的名字
char opt;
cin >> opt;
// 根据不同的名字说出不同的话
if (opt == '6') {
cout << "主人你好!" << endl;
cout << "今天你想吃什么" << endl;
}
else if (opt == '1') {
cout << "小一你好!" << endl;
cout << "今天天气怎么样?" << endl;
}
else if (opt == '2')
cout << "你有什么需要帮助的吗?" << endl;
else if (opt == '4' || opt == '5')
cout << "我不想跟你说话。" << endl;
else
cout << "我不太清楚你是谁..." << endl;
可以发现所有表示条件成立的表达式都是一样的形式:opt这个变量是否等于某个值。
针对这种情况,有一种专门的语法,叫做switch
语句。
switch 用法
switch
用于表示这样的分支结构:根据某个变量不同的值进行不同的操作。- 语法:
switch (变量名) {
case 变量可能的情况1: 执行语句1; break;
case 变量可能的情况2: 执行语句2; break;
...
// 可以有任意数量的 case 语句
default: 执行语句n;
}
- 语法解析
switch
后面的变量名:整型变量(比如int
或者char
),或者值为整型的表达式- 在一个
switch
中可以有任意数量的case
语句。 - 每个
case
后跟一个变量可能的值和一个冒号。这个变量可能的值必须与switch
中的变量具有相同的数据类型,而且是一个常量,比如1,2,3
或者‘A’,‘B’,‘C’
或者‘1’,‘2’,‘3’
等等。 - 当
switch
后面的变量等于case
后的常量时,case
后跟的所有语句将被执行,直到遇到break
语句为止。 break
(难点):- 当遇到
break
语句时,switch
终止,控制流将跳转到整个switch
语句后的下一行。 case
后可以没有break
, 如果某个case
后没有break
语句,程序就会接着往下执行。- 举个例子,假设 执行语句1之后没有
break
,那么如果变量名==
变量可能的情况1时,程序会执行执行语句1,然后接着执行 执行语句2,直到碰到break
。
- 举个例子,假设 执行语句1之后没有
- 当遇到
default
:一个switch
语句可以有一个可选的default
情况,出现在switch
的结尾。default
情况可用于上面所有case
都不为真时执行。
- 举例:
上一步所说的聊天机器人改写成switch语句是这样的:
char opt;
cin >> opt;
switch (opt) {
// 如果opt=='6',执行完两句cout后,遇见break,就会跳出整个switch
case '6':
cout << "主人你好!" << endl;
cout << "今天你想吃什么" << endl;
break;
case '1':
cout << "小一你好!" << endl;
cout << "今天天气怎么样?" << endl;
break;
case '2':
cout << "你有什么需要帮助的吗?" << endl;
break;
// 如果opt=='4',由于'4'的 case 后面没有 break
// 所以会往下执行'5'的 case 后面的语句,直到遇到 break
case '4': case '5':
cout << "我不想跟你说话" << endl;
break;
default:
cout << "我不太清楚你是谁..." << endl;
}
- Tips:对比这个场景下
if
和switch
的用法:- 如果用
if
来写会有如下的缺点:- 需要很多
opt == '1'
这样的关系表达式 - 一个分支下如果有多条语句,就需要加上大括号
- 需要很多
- 相比之下,switch就比较简洁。
- 如果用