&&和||
||和&&是我们经常用到的逻辑运算符,虽然简单,但毕竟容易犯错。
看例子:
int i = 0; int j = 0; if ((++i > 0) || (++j > 0)) { //打印出 i 和 j 的值。 }
结果:i=1;j=0。可能很多人会问,这是为什么呢?
这是因为||运算符是从左向右判断,一旦左边为真,则不再执行右边,整体为真;相反,只有左边为假,才会继续执行右边的表达式
那么,再来看看这个例子:
int i = 0; int j = 0; if ((++i < 0) && (++j > 0)) { //打印出 i 和 j 的值。 }
结果还是:i=1;j=0。这又是为什么呢?
这是因为&&运算符是从左向右判断,一旦左边为假,则不再执行右边,整体为假;相反只有左边为真,才会继续执行右边的表达式
两道练习题:
下列输出结果是什么?
int i = 1, j = 1, k = 2; if ((j++ || k++) && i++) { printf("%d,%d,%d\n", i, j, k); }
下列if语句,哪个判断条件为真?
int a = 1; int b = 3; if (a >= 2 || !b && b < 4) if(b - a && !a || a - b && a / b) if(a - !a % b && !b - !b % b) if(a + b && a - b && b - 3 && a || b)
++和--
基础
这绝对是一对让人头疼的兄弟。先来点简单的:
int i = 3; (++i)+(++i)+(++i);
上述表达式的值是多少?答案是18,因为 i 经过 3 次自加后变为 6,然后 3 个 6 相加得 18
++、--作为 前缀,我们知道是先自加或自减,然后再做别的运算;但是作为 后缀时,到
底什么时候自加、自减?这是很多初学者迷糊的地方。
假设 i=0,看 例子1:
j = (i++, i++, i++);
例子为逗号表达式,i 在遇到每个逗号后,认为本计算单位已经结束,i 这时候自加。 结果:i=3,j=2
例子2:
for (i = 0; i < 10; i++) { //code }
例子为for循环,与 10 进行比较之后,认为本计算单位已经结束,i 这时候自加。结果:i=10
例子3:
k = (i++) + (i++) + (i++);
例子 i 遇到分号才认为本计算单位已经结束,i 这时候自加。结果:k=0,i=3
也就是说后缀运算是在 本计算单位计算结束之后再自加或自减。C 语言里的计算单位大体分
为以上 3 类。
一道思考题:
下列代码输出的结果是什么?
int main() { int i = 0; for (i = 0, printf("First = %d\n", i); i < 10, printf("Second = %d\n", i); i++, printf("Third = %d\n", i)) { printf("Fourth = %d\n", i); } return 0; }
进阶
上面的例子很简单,那我们把括号去掉看看:
int i = 3; ++i+++i+++i;
天啦!这到底是什么东西?好,我们先看看这个: a+++b 和下面哪个表达式想当:
A),a++ +b;
B),a+ ++b;
贪心法
C 语言有这样一个规则: 每一个符号应该包含尽可能多的字符。从左到右一个一个字符地读入,判断 已经读入的字符串是否可能是一个符号的 组成部分,直到读入的字符组成的字符串已不再可能组成一个 有意义的符号。
注意,除了字符串与字符常量,符号的中间 不能嵌有空白(空格、制表符、换行符等)。比如:==是单个符号,而= =是两个等号。
按照这个规则可能很轻松的判断 a+++b 表达式与 a++ +b 一致。那么,回到上面的问题,结果应该是 (++i)++ + (i++) + i
那么,再请你独立思考 i+++++i 是多少?
/和%(负数整除与取模)
请先思考这个问题,2/(-2)的值为多少?2%(-2)的值呢?
好吧,我直接把它的规律告诉你吧
int main() { //负数整除与取模 //整除符合数学 printf("%d\n", 5 / 2);//2 printf("%d\n", -5 / 2);//-2 printf("%d\n", 5 / -2);//-2 printf("%d\n", -5 / -2);//2 //取模不符合,只看被取模数正负 printf("%d\n", 5 % 2);//1 printf("%d\n", -5 % 2);//-1 printf("%d\n", 5 % -2);//1 printf("%d\n", -5 % -2);//-1 return 0; }
整除符合整除数学公式,而取模不符合,只取决于被取模数的正负
那肯定有小伙伴会好奇,这是为什么啊?
这关于一些数学原理的实现,有兴趣的同学可以看看
假定我们让 a 除以 b,商为 q,余数为 r:
q = a/b;
r = a%b;
这里不妨先假定 b 大于 0。
我们希望 a、b、q、r 之间维持什么样的关系呢?
1,最重要的一点,我们希望 q*b + r == a,因为这是定义余数的关系。
2,如果我们改变 a 的正负号,我们希望 q 的符号也随之改变,但 q 的绝对值不会变。
3,当 b>0 时,我们希望保证 r>=0 且 r<b。
这三条性质是我们认为整数除法和余数操作所应该具备的。但是,很不幸,它们不可
能同时成立。
先考虑一个简单的例子:3/2,商为 1,余数也为 1。此时, 第一条性质得到了满足。
好,把例子稍微改写一下:(-3)/2 的值应该是多少呢?如果要满足 第二条性质,答案应该是-1。
但是,如果是这样,余数就必定是-1,这样 第三条性质就无法满足了。如果我们首先满足 第三条性质,即余数是 1,这种情况下根据 第一条性质,商应该为-2,那么 第二条性质又无法满足了。
上面的矛盾似乎无法解决。因此,C 语言或者其他语言在实现整数除法截断运算时,必
须放弃上述三条性质中的至少一条。大多数编程语言选择了放弃第三条,而改为要求余数与
被除数的正负号相同。这样性质 1 和性质 2 就可以得到满足。大多数 C 语言编译器也都是
如此。
但是,C 语言的定义只保证了性质 1,以及当 a>=0 且 b>0 时,保证|r|<|b|以及 r>=0。后
面部分的保证与性质 2 或性质 3 比较起来,限制性要弱得多。
总结
这是一些关于易错运算符(包括&&和||、++和--以及贪心法、负数取模)的整理与分享,希望对各位有所帮助~