LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1010|回复: 11

抱歉斑竹,有关(++i)+(++i)+(++i)的贴子,想再讨论

[复制链接]
发表于 2006-1-11 16:47:48 | 显示全部楼层 |阅读模式
http://www.linuxsir.cn/bbs/showthread.php?t=232895

虽然已经锁了,先声明,我并不是讨论是12,还是13,还是15,而是想说,这是否是
GCC和VC的BUG?

kinton帅哥提到,Java下是12,我觉得这个是完全遵从标准的运算结果(实现是左++先执行时)。

根据C标准,++运算符应该返回rvalue,也就是代入右值,所以像下面的写法是不合法的
++i = 100;
根据各个运算符的标准描述,(++i)+(++i)应该是翻译成下面的序列
temp1 = ++i; /* i = 3 */
temp2 = ++i; /* i = 4 */
temp1+temp2  /* 7 */

看了GCC生成的汇编代码,却是
lea    0xfffffffc(%ebp),%eax
incl   (%eax)
lea    0xfffffffc(%ebp),%eax
incl   (%eax)
mov    0xfffffffc(%ebp),%eax
mov    0xfffffffc(%ebp),%edx
add    %eax,%edx
也就是说,生成的运算顺序是
++i;/* i = 3 */
++i;/* i = 4 */
temp1 = i;/* temp1 = 4 */
temp2 = i;/* temp1 = 4 */
temp1 + temp2 /* 8 */

我的疑问是,这是否是GCC的BUG,也就是没有完全遵从C标准?

同样,下面两个程序,会得出不一样的结果
int main()
{
        int i;
        int r;
        i = 1;
        r = (i+=1) + (i+=2);
       
        return 0;
}

int main()
{
        int i;
        int r;
        r = (i=2) + (i=3);
       
        return 0;
}

头一个r最后是6,下一个r是5。

-----------------------------------------------------------
最后声明一下,我不是讨论程序是不是应该这么写,上面举的例子全是
最糟糕的书写方式,我是想讨论gcc是否没有完全遵从c标准。
发表于 2006-1-11 16:58:02 | 显示全部楼层
这并不是什么Bug。
这是C语言就这么说的,这样的表达式里面各个运算的先后顺序是未定义的,所以写出这样的语句是完全不具有可移植性的,不同的编译器会有不同的结果。
回复 支持 反对

使用道具 举报

发表于 2006-1-11 18:13:07 | 显示全部楼层
楼主看来是对C标准还是很熟悉的样子,但是这儿不仅仅是与++运算有关,这是与整个表达式的求值顺序有关,而这是未定义的,不知道有没有看过《The C Programming Language》,这是我从中抄下来的一段:

  1. [The C Programming Language(2e)
  2.      <2.12 Precedence and Order of Evaluation> P.52]
  3.   C, like most languages, does not specify the order in which
  4. the operands of an operator are evaluated. (The exceptions are
  5. &&, ||, ?:, and ','.) For example, in a statement like
  6.     x = f() + g();
  7. f may be evaluated before g or vice versa; thus if either f or
  8. alters a variable on which the other depends, x can depend on
  9. the order of evaluation. Intermediate results can be stored in
  10. temporary variables to ensure a particular sequence.
  11.   Similarly, the order in which function arguments are evaluated
  12. is not specified, so the statement
  13.     printf("%d %d\n", ++n, power(2,n));  /* WRONG */
  14. can produce different result with different compilers, depending
  15. on whether n is incremented before power is called. The solution,
  16. of course, is to write
  17.     ++n;
  18.     printf("%d %d\n", n, power(2, n));
  19.   Function calls, nested assignment statements, and increment and
  20. decrement operators cause "side effects"--some variable is changed
  21. as a by-product of the evaluation of an expression. In any
  22. expression involving side effects, there can be subtle dependencies
  23. on the order in which variables taking part in the expression are
  24. updated. On unhappy situation is typified by the statement
  25.     a[i] = i++;
  26. The question is whether the subscript is the old value of i or the
  27. new. Compilers can interpret this in different ways, and generate
  28. different answers depending on their interpretation. The standard
  29. intentionally leaves most such matters unspecified. When side
  30. effects (assignment to variables) take place within an expression
  31. is left to the discretion of the compiler, since the best order
  32. depends strongly on machine architecture. (The standard does specify
  33. that all side effects on arguments take effect before a function is
  34. called, but that would not help in the call to printf above.)
  35.   The moral is that writing code that depends on order of evaluation
  36. is a bad programming practice in any language. Naturally, it is
  37. necessary to know what tings to avoid, but if you don't know how they
  38. are done on various machines, you won't be tempted to take advantage
  39. of a particular implementation.
复制代码
回复 支持 反对

使用道具 举报

发表于 2006-1-11 19:13:09 | 显示全部楼层
按上面这么说,是不是这样也存在同样的问题?

#include <stdio.h>

int i = 2;

int f(void)
{
        return i += 2;
}

int g(void)
{
        return i *= 2;
}

int main(void)
{
        printf("%d\r\n", g() + f());

        return 0;
}
回复 支持 反对

使用道具 举报

发表于 2006-1-11 21:21:09 | 显示全部楼层
《C和指针》P.84
  1. main()
  2. {
  3.     int i = 10;
  4.     i = i-- - --i * (i = -3) * i++ + ++i;
  5.     printf("i = %d\n", i);
  6. }
复制代码
结果:
  1. -128    Tandy 6000 Xenix 3.2
  2. -95      Think C 5.02(Macintosh)
  3. -86      IBM PowerPC AIX 3.2.5
  4. -85      Sun Sparc cc(K&R C Compiler)
  5. -63      gcc, HP_UX 9.0, Power C 2.0.0
  6. 4         Sun Sparc acc(K&R C Compiler)
  7. 21       Turbo C/C++ 4.5
  8. 22       FreeBSD 2.1R
  9. 30       Dec Alpha OSF1 2.0
  10. 36       Dec VAX/VMS
  11. 42       Microsoft C 5.1
复制代码
我的机器gcc version 4.0.3 20060104 (prerelease) (Debian 4.0.2-6)结果是22.
回复 支持 反对

使用道具 举报

发表于 2006-1-11 21:26:42 | 显示全部楼层
个人感觉这种代码根本没有意义。
写出来,别说别人看不懂,自己也看不懂啊。
回复 支持 反对

使用道具 举报

发表于 2006-1-11 22:06:08 | 显示全部楼层
在 谭浩强 的 C++程序设计 上也提到了,他的意思也是不同的编译器可能做出不同的行为,所以只要不用就可以了。
回复 支持 反对

使用道具 举报

发表于 2006-1-12 10:03:41 | 显示全部楼层
Post by waa
按上面这么说,是不是这样也存在同样的问题?

#include <stdio.h>

int i = 2;

int f(void)
{
        return i += 2;
}

int g(void)
{
        return i *= 2;
}

int main(void)
{
        printf("%d\r\n", g() + f());

        return 0;
}



如果改成这样,真的有问题了
int main(void)
{
        printf("%d %d\r\n", f(), g());

        return 0;
}
回复 支持 反对

使用道具 举报

发表于 2006-1-12 11:47:18 | 显示全部楼层
个人认为undefined behavior没什么可讨论的(编译器想怎么着就怎么着,你还讨论什么)。
回复 支持 反对

使用道具 举报

发表于 2006-1-12 13:01:16 | 显示全部楼层
呵呵,兄弟真执着啊。
这个帖子开绿灯,大家随便聊把
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表