(转)(i++)+(++i)+(i++),罪恶的语句!

来源:互联网 发布:深圳电商公司知乎 编辑:程序博客网 时间:2024/04/28 19:49

 原文链接:http://skylersun.info/363 

源代码是程序员之间的交流,不是程序员之间的互打哑谜。───题记

这是郑晖老师的话,我引为题记。我想,这里的读者一定会有大学生,在学C语言时,一定遇到过许多求“(i++)+(++i)+(i++)”之类的问题。昨天有人拿这题来问我,令我一时语塞,研究了半天。今天我得说清楚,这样的语句是罪恶的。也许有人说,这明明是考试重点呀。别急,下面我们分成三个部分来组织:在第一部分中,解释为什么它有罪;第二部分,我们将错就错,找出一般的解题规律;在第三部分中,给出几个更变态的题目,今后谁出这种题给你,你就用我这里的题目去问他。

一.  这种语句的合法性

C语言的创始人D.M.R在《CPL》中明确写道:

自增与自减运算符只能作用于变量,类似于表达式(i+j)++是非法的。

自增运算实际上包括了一个赋值运算,而上面的表达式不是赋值运算的左值,因为它没有确定的内存地址。只要你能理解 (i+j)=5 是非法的,那就不难理解为什么 (i+j)++ 非法了。以此为依据,(i++)+(i++) 这个语句中的后一项,是非法的。尽管看起来它只是一个 i++,但别忘了,在前一个 i++ 的作用下,后面的 “i” 本身就已经不是单个变量了,不能再做++。所以,一般地,在一个语句中,对同一个变量调用多次自增或自减运算,都是非法的。D.M.R还提醒,编译器应在这种情况下给出警告。事实上,gcc 确实会对此给出一个:

Warning: operation on ‘i’ may be undefined

这已经够清楚了,无需多言。请编写教学大纲者注意,多年以来,你们一直在用非法语句作考试重点,将可爱的知识变得十分可恶。

二.  如何计算

尽管它非法,但同学们没有执法权,只能任其蹂躏。所以,我们探索一下,GCC 编译器(你们考试的答案通常以此为准)在摸不到头脑的情况下,是如何处理这个语句的。我们以 (i++)+(++i)+(++i)+(i++)+(i++) 为例,先将其编译成汇编代码,结果是这样的:

  1. incl    -4(%rbp)  
  2. movl    -4(%rbp), %eax  
  3. addl    -4(%rbp), %eax  
  4. incl    -4(%rbp)  
  5. addl    -4(%rbp), %eax  
  6. addl    -4(%rbp), %eax  
  7. addl    -4(%rbp), %eax  
  8. incl    -4(%rbp)  
  9. incl    -4(%rbp)  
  10. incl    -4(%rbp)  

为了照顾看不懂汇编的朋友,我把它直接对应着翻译成C语言。用变量sum表示要求的和,就是下面这个样子:

  1. i=i+1;  
  2. sum=i;  
  3. sum=sum+i;  
  4. i=i+1;  
  5. sum=sum+i;  
  6. sum=sum+i;  
  7. sum=sum+i;  
  8. i=i+1;  
  9. i=i+1;  
  10. i=i+1  

这段代码,能帮助你理解这个过程,不妨自己思考一番。下面给出我分析的结果:

1. 先将所有的 i++ 改成 i ,然后在整个语句的最后,统一将 i 自增相应的次数(语句中有几个 i++,就在最后自增几次)。我们的例子,这时就会变成 i+(++i)+(++i)+i+i; i++; i++; i++;
2. 按照加法的结合性,先将左起前两项相加。如果前两项中含有 ++i,则先算 ++i;
3. 前两项的和作为一项,与第三项相加,以此类推。同样,遇到 ++i,就先算 ++i。

以上就是 gcc 编译器处理这种语句的规律,当然,这并不是 C 语言定义的,只是编译器在出错情况下的无奈之举。大家可以用这个方法来解题,下面我就回答昨天一位同学问我的问题:

i=5; 求 (i++)+(++i)+(i++) 的值

按上面的方法做:
1. 先把i++换到最后面,变成 i + (++i) + i ; i++; i++;
2. 从左到右累加,先拿出前两项 i + (++i)。先算 ++i,i 的值变成6,两项相加得12;
3、计算 12 +i,得18。

三.  以牙还牙

我们的教育者既然喜欢出这种题目,以我的分析,恐怕是有一种变态癖。既然这样,我就满足一下他们。读者朋友,以后谁给你出这种题,你就用下面的题目去问他。(要感谢郑晖老师的贡献)

1. 写出下面语句的运行结果

view plaincopy to clipboardprint?
  1. printf("printf(%d)",printf("%d",printf("%d",printf)));  

2:写出下面函数的值

view plaincopy to clipboardprint?
  1. int i=5;  
  2. pow(i+(++i)+(i++), (i+(--i)+(i--), i+(++i)+(i--)));  

3:写出下面程序的运行结果

view plaincopy to clipboardprint?
  1. main()  
  2. {  
  3.     int x=0,y[14],*z=*(&y);*(z++)=0x46;*(z++)=y[x++]+0x0F;  
  4.     *(z++)=y[x++]-0x12;*(z++)=y[x++]+0x08;*(z++)=y[x]-0x4B;  
  5.     x=*(--z);while(y[x]!=NULL) putchar(y[x++]);  
  6. }  

原创粉丝点击