C ++与Java的评估顺序

时间:2014-02-25 01:53:35

标签: java c++ operator-precedence

这是关于另一个question: Java Recursion Bug? I am going crazy

我理解那里的解决方案。但是,为什么 C ++ 在这种情况下的行为与Java不同?

任何人都可以给C ++ / Java规范提供准确的指针(没有双关语)吗?我知道java在每次调用之前将sum赋值为sum,而C ++则以不同的方式执行。但是允许这个的规范是什么?

编辑: 从链接添加代码

public class Test {

public static int sum=0;

public static int fun(int n) {

    if (n == 1)
        return 1;
    else
        sum += fun(n - 1);  // this statement leads to weird output
    // { // the following block has right output
    //     int tmp = fun(n - 1);
    //     sum += tmp;
    // }

    return sum;
}

public static void main(String[] arg) {
    System.out.print(fun(5));
}
}

输出为1,应为8.相对C / C ++代码如下:

#include<stdio.h>
int sum=0;
int fun(int n) {

    if (n == 1)
        return 1;
    else
        sum += fun(n - 1);

    return sum;
}

int main()
{
printf("%d",fun(5));

return 0;
}

C ++ 中的输出为8

3 个答案:

答案 0 :(得分:3)

好的,结合每个人的评论:

这一行:

sum += fun(n - 1);  // this statement leads to weird output

扩展为

sum = sum + fun(n - 1);

在C ++和Java中。请参阅JLS 15.26.2C++11 draft第5.17(7)节。

在Java中,该语言指定在调用函数之前必须首先计算sum。见JLS 15.7.1。为了做到这一点(如果fun修改sum),代码必须在sum调用fun之前阅读fun并将其保存在某处。在sum返回后,代码会将保存的fun版本添加到sum的结果中。由于fun永远不会在所有sum调用开始之后被修改,因此结果是所有已保存的+版本都为0,结果为1.

在C ++中,未指定评估sum的操作数的顺序。 (参见C ++ 11草案的1.9.15。)因此,函数结果可能是1或8,具体取决于编译器决定如何实现它。 8可能更有可能,因为编译器可能会生成不需要sum保存在临时中的代码,并且在某些处理器上可以生成直接添加到{{1}}而不读取它的指令第一。但是1不会是不正确的结果,因为评估顺序未指定,结果可能会根据评估顺序而改变。道德:不要写这样的代码。

答案 1 :(得分:1)

来自jls http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.7.1

  

<强> 15.7.1。首先评估左手操作数

     

在评估右侧操作数的任何部分之前,二元运算符的左侧操作数似乎已完全评估。

     

如果运算符是复合赋值运算符(第15.26.2节),则对左侧操作数的计算包括记住左侧操作数表示的变量并获取并保存该变量的值以供在隐含的二元操作。

     

如果对二元运算符的左侧操作数的求值突然完成,则右侧操作数的任何部分似乎都没有被评估过。

代表

sum += fun(n - 1);
首先评估总和,并保存值0对sum值的任何更改 在此之后被忽略。

然后评估乐趣,等于1

给出结果

sum = 0 + 1 

递归地这与

相同
sum = sum + sum + sum + sum + 1

sum = 0 + 0 + 0 + 0 + 1

答案 2 :(得分:0)

update - 将sum声明为volatile将阻止在表达式中使用sum的缓存副本,但这可能无法解决评估问题的顺序。

如果Java总是从左到右,那么将语句更改为sum = fun(n-1)+ sum应该有效,但原始示例中提到的替代代码可能是唯一的解决方案。在这个例子中不需要volatile,但我把它放在那里以显示它是如何实现的。

#include <iostream>

class Test{
public:
static volatile int sum;
static int fun(int n) {
    if (n == 1)
        return 1;
    int tmp = fun(n - 1);
    sum += tmp;
    return sum;
}
};

volatile int Test::sum = 0; // declare and initialize sum

int main(){
    std::cout << Test::fun(5) << std::endl;
    return(0);
}