易错的运算符

简介: 易错的运算符

&&和||


 ||和&&是我们经常用到的逻辑运算符,虽然简单,但毕竟容易犯错。

 看例子:

  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 比较起来,限制性要弱得多。


总结

这是一些关于易错运算符(包括&&和||、++和--以及贪心法、负数取模)的整理与分享,希望对各位有所帮助~

相关文章
|
21天前
|
C语言
操作符详解(3)C语言复合赋值操作符单目操作符
操作符详解(3)C语言复合赋值操作符单目操作符
|
8月前
|
存储 C语言
C语言操作符[算数操作符,赋值操作符,单目操作符,移位操作符]
C语言操作符[算数操作符,赋值操作符,单目操作符,移位操作符]
|
21天前
|
C++
在C++语言中自增自减运算符
在C++语言中自增自减运算符
12 0
|
9月前
|
存储 编译器 C语言
初始C语言——详细讲解操作符以及操作符的易错点
初始C语言——详细讲解操作符以及操作符的易错点
|
11月前
|
存储 编译器 C语言
【C语言初阶】带你轻松玩转所有常用操作符(1) ——算数操作符,移位操作符,位操作符
【C语言初阶】带你轻松玩转所有常用操作符(1) ——算数操作符,移位操作符,位操作符
63 0
|
7月前
|
编译器 C语言
C语言之操作符表达式求值篇
C语言之操作符表达式求值篇
46 0
|
8月前
|
编译器 C语言
操作符的属性,C语言中运算符的优先性和结合性,常见的问题表达式
操作符的属性,C语言中运算符的优先性和结合性,常见的问题表达式
|
11月前
|
编译器 C语言 C++
学C的第十六天【操作符详解:9. 条件操作符;10. 逗号表达式;11. 下标引用,函数调用和结构函数;12.表达式求值:整型提升、算术转换、操作符的属性;练习:使用函数完成整型函数的打印、元素逆置】-2
12.表达式求值 1. 表达式求值的顺序一部分是由操作符的优先级和结合性决定。 2. 有些表达式的操作数在求值的过程中可能需要转换为其它类型。

热门文章

最新文章