为什么不同的C++编译器会对这个代码给出不同的结果? 我正在编写一些C++代码来进行有趣和实践,以了解更多关于语言特征的代码。我想更多地了解静态变量及其在递归函数中的行为。在g++编译器中尝试此代码,得到预期结果: #include <iostream> using namespace std; int f(const int& value) { static int result = 0; return result += value; } int main() { cout << f(10) << ", " << f(f(10)) << ", " << f(f(f(10))); return 0; }

为什么不同的C++编译器会对这个代码给出不同的结果? 我正在编写一些C++代码来进行有趣和实践,以了解更多关于语言特征的代码。我想更多地了解静态变量及其在递归函数中的行为。在g++编译器中尝试此代码,得到预期结果: #include <iostream> using namespace std; int f(const int& value) { static int result = 0; return result += value; } int main() { cout << f(10) << ", " << f(f(10)) << ", " << f(f(f(10))); return 0; },c++,visual-c++,expression-evaluation,unspecified-behavior,C++,Visual C++,Expression Evaluation,Unspecified Behavior,为什么MSVC的输出不同?未指定以下三个子表达式的求值顺序: f(10) f(f(10)) f(f(f(10))) 编译器可以按任何顺序计算这些子表达式。您不应该依赖程序中特定的求值顺序,尤其是当您打算使用多个编译器进行编译时 这是因为该表达式中的任何位置都没有序列点。唯一的要求是在需要结果之前,即在打印结果之前,对这些子表达式中的每一个子表达式求值 在您的示例中,实际上有几个子表达式,我在这里将其标记为a到k: // a b c d e f g

为什么MSVC的输出不同?

未指定以下三个子表达式的求值顺序:

f(10)
f(f(10))
f(f(f(10)))
编译器可以按任何顺序计算这些子表达式。您不应该依赖程序中特定的求值顺序,尤其是当您打算使用多个编译器进行编译时

这是因为该表达式中的任何位置都没有序列点。唯一的要求是在需要结果之前,即在打印结果之前,对这些子表达式中的每一个子表达式求值

在您的示例中,实际上有几个子表达式,我在这里将其标记为a到k:

//   a  b     c       d  e f      g       h  i j k
cout << f(10) << ", " << f(f(10)) << ", " << f(f(f(10)));

调用运算符未指定以下三个子表达式的求值顺序:

f(10)
f(f(10))
f(f(f(10)))
编译器可以按任何顺序计算这些子表达式。您不应该依赖程序中特定的求值顺序,尤其是当您打算使用多个编译器进行编译时

这是因为该表达式中的任何位置都没有序列点。唯一的要求是在需要结果之前,即在打印结果之前,对这些子表达式中的每一个子表达式求值

在您的示例中,实际上有几个子表达式,我在这里将其标记为a到k:

//   a  b     c       d  e f      g       h  i j k
cout << f(10) << ", " << f(f(10)) << ", " << f(f(f(10)));

仅仅因为输出从左到右出现在屏幕上,就调用操作符,这并不意味着计算顺序遵循相同的方向。在C++中,函数参数的求值顺序未指定。另外,仅仅因为输出从左到右显示在屏幕上,就通过打印数据并不意味着评估顺序遵循相同的方向。在C++中,函数参数的求值顺序未指定。另外,通过打印数据是一种简单的方法,可以准确地看到它在做什么:

int f(const int& value, int fID)
{
   static int result = 0;
   static int fCounter = 0;
   fCounter++;
   cout << fCounter << ".  ID:" << fID << endl;    
   return result += value;
}

int main()
{
   cout << f(10, 6) << ", " << f(f(10, 4), 5) << ", " << f(f(f(10, 1),2),3);
   return 0;
}

我同意其他人在回答中所说的,但这会让你确切地看到它在做什么

一种简单的方法,可以准确地看到它在做什么:

int f(const int& value, int fID)
{
   static int result = 0;
   static int fCounter = 0;
   fCounter++;
   cout << fCounter << ".  ID:" << fID << endl;    
   return result += value;
}

int main()
{
   cout << f(10, 6) << ", " << f(f(10, 4), 5) << ", " << f(f(f(10, 1),2),3);
   return 0;
}

我同意其他人在回答中所说的,但这会让你确切地看到它在做什么

前缀运算符语法转换为以下前缀表示法:

<<( <<( <<( cout, f(10) ), f(f(10)) ), f(f(f(10))) )
 A   B   C
对于每个调用,编译器都可以按任意顺序计算参数,为了正确计算A的第一个参数,必须首先计算B,同样,对于B的第一个参数,必须计算整个C表达式。这意味着第一个参数依赖项所要求的a、B和C的执行有一个偏序。对每个调用和两个参数的求值也有偏序,因此引用调用B的第一个和第二个参数的B1和B2必须在B之前求值

这些偏序不会导致调用执行的唯一要求,因为编译器可以在尝试计算第一个参数之前决定执行所有第二个参数,从而导致等效路径:

tmp1 = f(10); tmp2 = f(f(10)); tmp3 = f(f(f(10)));
cout << tmp1 << tmp2 << tmp3;


或者。。。保持组合。

前缀运算符语法转换为以下前缀符号:

<<( <<( <<( cout, f(10) ), f(f(10)) ), f(f(f(10))) )
 A   B   C
对于每个调用,编译器都可以按任意顺序计算参数,为了正确计算A的第一个参数,必须首先计算B,同样,对于B的第一个参数,必须计算整个C表达式。这意味着第一个参数依赖项所要求的a、B和C的执行有一个偏序。对每个调用和两个参数的求值也有偏序,因此引用调用B的第一个和第二个参数的B1和B2必须在B之前求值

这些偏序不会导致调用执行的唯一要求,因为编译器可以在尝试计算第一个参数之前决定执行所有第二个参数,从而导致等效路径:

tmp1 = f(10); tmp2 = f(f(10)); tmp3 = f(f(f(10)));
cout << tmp1 << tmp2 << tmp3;


或者。。。继续组合。

我认为这不仅仅与@Sor有关:不知道你的意思。语句总是按顺序执行的。我认为这不仅仅与@Sor有关:不知道你的意思。语句总是按顺序执行。它将准确地显示它正在执行的操作,但不能保证在下一次编译相同代码时得到相同的结果。它将准确地显示它正在执行的操作,但它将 我不保证下次编译相同的代码时会得到相同的结果。更糟糕的是,在fff10中,对f的各种调用可能会以任何顺序与此函数调用之外对f的其他调用一起发生。换句话说,编译器可以计算f10 3x,然后计算f10 2x中的fresult_,然后计算f10中的fresult_…或其任何组合。更糟糕的是,在fff10中,对f的各种调用可以与此函数调用之外的对f的其他调用以任何顺序同时发生。换句话说,编译器可以计算f10 3x,然后计算f10 2x中的fresult_,然后计算f10中的fresult_……或其任意组合。