一、逻辑表达式
表达出的结果为真或者假,也就是bool值的true或者false,
关系运算符
符号 | 含义 |
< | 小于 |
> | 大于 |
<= | 小于等于 |
>= | 大于等于 |
关系运算符返回值只有bool值的true或者false,关系运算符优先级都低于算术运算符。
如a+b < c+1
实际运算顺序为(a+b)<(c+1)
并且关系运算符都是左结合的
注意!!!:
a<b<c在C语言中是合法的,但这个并不是判断b是否处于a与c之间,因为关系运算符是左结合的,那么,实际上等价于
(a<b)<c
因为a和b返回的值一定为0或1 ,所以最后会变为c是否大于0或1
判等运算符
顾名思义,就是判断运算符两边的表达式是否相符,同样返回bool值0;1。
注意,判等运算符是“==”。
符号 | 含义 |
== | 等于 |
!= | 不等于 |
还需要注意的是,判等运算符也是左结合的,同样,在一个表达式中接连使用两个判等运算符也是可以的,不过同样不建议这么写
开发中要避免写出别人难以阅读的代码!!!!
逻辑运算符
符号 | 含义 |
! | 逻辑非(一元) |
&& | 逻辑与(二元) |
|| | 逻辑或(二元) |
!:放在表达式前面,用于给表达式取反,如果当前表达式的结果为0,取反运算的结果就为1,反之为1的话结果就为0;
同样要注意的一种用法
int main()
{
int a = 20;
printf("bbbbbbbbbbbbbbb %d \n",!a);
return 0;
}
如上代码输出:bbbbbbbbbbbbbbb 0
因为在C语言中,任意非0数字都相当于true(即使是负数,甚至是浮点数也满足这个条件),所以对20取反得到的为0,但对0取反我们只能得到1;
同样在实际当中一定要避免此写法
&&:如果表达式1和表达式2都是非0值则返回1,只要有一个为0值则返回0
||:如果表达式1和表达式2至少有一个为非零值,则返回1,两个都为0值则返回0;
运算&&、||这种二元运算符时都对操作数进行从左到右的“短路”计算,也就是说,如果我们在做操作数上已经能够确定表达式的值时,就会忽略右表达式的计算结果,
如当
a = 0;b = 1;时
a&&b 在运行完a等于0这个0值时,直接判断&&结果为false
b&&a 在b行完a等于1这个1值时,直接判断&&结果为true
编译器这样做,无疑是为了提高运行效率。但对于右值(表达式)来说就比较危险了,如果我们右值隐藏了一些错误,或者一些重要操作容易被忽略
int main()
{
int a =0;
a&&5/0;
return 0;
}
如上方代码,当a等于0时为false,程序不会报错,但如果a为1时会出现数学异常。
一、if
if语句需要配合逻辑表达式
if(逻辑表达式)
{
//真 dosm 可以执行整个当前if{}作用域内的分支
}
else
{
//假 dosm 可以执行整个当前else{}作用域内的分支
}
if(逻辑表达式)本身是一个完整的语句,其实后面可以什么都不写
当逻辑表达式的结果为真(true 在C语言中一般为1或者其他正整数),则执行if作用域内代码,
if语句还有其他写法
int main()
{
//第一种
if(1)
{
printf("hahah");
}
//第二种
if(1)
printf("hahah");
}
if(逻辑表达式) 后面如果不接{}作用域,那么if(逻辑表达式) 成立后就会执行if(逻辑表达式) 下一行语句,第一个;之前的代码
也就是说
if(0)
printf("aaaa/n");
printf("bbbb/n");
if(0)
printf("aaaa/n");printf("bbbb/n");
上面两种写法输出的只有
bbbb
else也有这种作用,区别时,else必须配合if使用
同时要注意
表达式判等时不要少写==
如
if(a == 20)写成if(a = 20)
这种表达式的结果只有true,因为,第一个的表达式是判等结果的bool值,而 第二种写法的表达式结果是a,当a为0时一定是false,当a为非零值一定为true,相当于
第二种写法相当于
if(a != 0)
这与我们期待实现的if(a == 20)大相径庭
并且这种错误不会报错,查起来比较麻烦,所以一定要细心,避免这种错误发生
当然,除了细心还有可以通过编写代码的手法来避免此问题的发生
如
if(20 == a)
利用常量不能赋值的特性,让编译器替我们检查书写错误,当我们少写一个=时
产生
20 = a
编译器会发生报错,提醒我们,当然这种方法也有局限性,注意只有常量和变量比较时才适用,尽量保持养成这种写法
级联式if语句
if(表达式1) { printf("aa\n"); } else if(表达式2) { printf("bb\n"); } else if(表达式3) { printf("cc\n"); } else { printf("dd\n"); }
程序会从上到下依次判断表达式,判断结束后执行对应作用域
“悬空else”问题
之前写道,if和else都会执行语句后面第一个{}内的语句或者第一个语句,但有一种情况需要注意一下
int main()
{
int a =1;
if(1)
int a =1;
if(0)
int b =1;
else//这个else属于哪个if?
printf("6666\n");
}
从缩进上来看是第一个,但实际上,else永远与最近但未匹配其他else的if进行匹配,也就是上方第二个,虽然我们知道原理,但这样写代码会导致程序阅读性很差,建议写if、else一定要加作用域{}保证程序可读性。
二、条件表达式(三元运算符)
表达式1?表达式2:表达式3
三个表达式可以是任意类型
又因为这个表达式是C语言中唯一需要三个操作数的运算符,所以才被称为三元运算符或三目运算符
如果表达式1成立,则三元运算符的结果为表达式2的值,否则三元运算符的结果为表达式.3的值
三、Switch语句
Switch语句主要是为了代替级联式if语句 处理一些整型表达式
switch (整型表达式) {
case 常量整型表达式1:
执行语句
break;
case 常量整型表达式2://之后可以无限追加
执行语句
break;
default:
执行语句
break;
}
只需要注意以下5点,就可以完全理解这个语句了
1.首先整型表达式可以使任何能产生整形的函数或任意表达式(可以为不确定的值)
2.常量整型表达式 要求是必须为整数且为常量(包括宏常量这种),不能为不确定的值
3.default:代表其他值,当当前所有case 常量整形表达式: 均不满足时将执行default:后面的语句
4.当整型表达式 满足 case 中的 常量整型表达式 时就会执行这个位置的case :到 后面第一个break;前的所有代码 (注意 即使后面的含有不符合的 case 常量整型表达式 也会被执行 直到遇到break;)
5.default:分支不一定要放在最后(但开发习惯上,我们要保持将default放在函数末尾的习惯)
由于以上几个问题大部分编译器可以帮我们验证,所以直接从第4点开始验证
代码如下
#include <stdio.h>
#include <stdbool.h>
int main()
{
switch (2) {
case 1:
printf("11111111111 \n");
// break;
case 2:
printf("22222222222 \n");
// break;
case 3:
printf("33333333333 \n");
// break;
default:
printf("44444444444 \n");
break;
}
return 0;
}
输出如下
22222222222
33333333333
44444444444
可以看出case3和default:完全不满足,但还是执行了
这里就引申出了一个潜在问题和一个常用用法
潜在问题:
当我们本意是执行case2,但case2后忘记写break; 那么这个错误可能导致多执行几行不相干的语句(如执行输出33333333333)
其他用法:
当出现其他同质化的期待结果时,我们可以利用这种机制少写很多代码
int a = 2;//今天星期2
switch (a) {
case 1:case 2:case 4:
printf("加大班 \n");
break;
case 3:case 5:case 6:case 7:
printf("玩大游戏 \n");
break;
default:
printf("头七 \n");
break;
}
比如,鄙人 星期一二四加大班子 没必要分出case 多写一堆printf("加大班 \n");
告诉我今天星期几就好了,复用性将得到大大增强,这一点就是利用了break;跳出机制在合适的地方省略了几个break;不然加完大班再熬夜再玩大游戏 ,那可真的要直接过头七了(手动狗头)...
所以当我明确好需求之后写完代码一定要检查好我们的跳出是否正常!!!
再讲个比较有意思的地方
default:这行语句,在Switch作用域内,不论你将它放在哪一级,他都是最后执行,并且不会影响其他功能
我们可以来验证一下:
#include <stdio.h>
#include <stdbool.h>
int main()
{
switch (3) {
case 1:
printf("11111111111 \n");
break;
default:
printf("44444444444 \n");
break;
case 2:
printf("22222222222 \n");
break;
case 3:
printf("33333333333 \n");
break;
}
return 0;
}
输出
printf("33333333333 \n");
很明显,Switch语句不会按顺序来处理default: 语句,而是放到2最后进行处理,即使你将default: 后面的beak;去掉也没有影响
但是有一点比较令我奇怪的地方,当我们将 case 3:后面的break;去掉,是不会输出default:的内容的,所以只能说明case未满足才会执行default。
四、bool值
首先C89中没有bool这种类型,所有的真或者假都是开发者自己定义的
如
#define TRUE 1
#define FALSE 0
直到C99也没有出现真正意义上的bool值,但出现了一个叫做_Bool 的无符号整型变量
#include <stdio.h>
int main()
{
_Bool a = 2;
printf("_Bool! == %d \n",a);
return 0;
}
这个类型只要不赋值为为0就会强转为1
后面还出现了类型名也叫bool的宏,这个值的两个flag分别为true和false,是现在大部分语言bool值的雏形了
这个定义需要引入stdbool.h头文件才能使用
#include <stdio.h>
#include <stdbool.h>//需要引入此头文件
int main()
{
bool a = true;
bool b = false;
printf("_Boola == %d _Boolb == %d \n",a,b);
return 0;
}
输出
_Boola == 1 _Boolb == 0
五、break跳出
在这里只讲在Switch中的break,break的作用是是Switch语句的计算跳出Switch语句