2017-02-07 7 views
0

私は次のコードがあります評価順*

int p = 5; 
std::cout << f(p, p) << std::endl; 

xが渡されているので出力は、9^4次のとおりです。私はこのような上記の関数を呼び出すと仮定し、今

int f(int &x, int c){ 
    c = c - 1; 
    if (c == 0) return 1; 
    x = x + 1; 
    return f(x, c)*x; 
} 

をしたがって、xの最終値は9でなければならないが、上記の機能のreturnステートメントが次のように変更された場合:

return x*f(x, c); 

出力が3024 (6*7*8*9)です。なぜ出力に違いがありますか? Operator*の評価の順序とは関係がありますか?上記のコードの出力を予測するように頼まれた場合、コンパイラ依存か不定で固定されていますか?

+0

"上記のコードの出力を予測するように求められたら、それは固定されているのか、コンパイラに依存していますか? - それは固定され、またコンパイラに依存でもないですが、それは未指定ですが、[ここ](http://en.cppreference.com/w/cpp/language/eval_order)を参照してください。あなたはそれに頼るべきではありません。コンパイラは、計算を優先して並べ替えることができます。 – yeputons

+0

あなたは基本的にやっていることは、逆の順序 – macroland

+0

で階乗関数である@macroland私はそうは思わない、それは違います。 – Jarvis

答えて

2

あなたが書いた:

f(x,c)*x 

コンパイラは、前またはfを呼び出した後のいずれか(第2オペランド用)xで保存された値を取得することもできます。したがって、実行が進む可能性のある多くの方法があります。コンパイラーは、この選択において一貫性を使用する必要はありません。

あなたは書くことができる問題を回避するには、次の

auto x_temp = x; 
return f(x, c) * x_temp; 

注:これは、不特定の動作です。関数呼び出しの前後にシーケンスポイントがあるため(またはC++ 11の用語では、関数内のステートメントは、呼び出しコードに関して順序付けされていない、順序付けされていないため)、未定義の動作ではありません。

+0

' return f(x、c) 'と同じ出力を' return x * f(x、 c)* x'他のコンパイラを使う? – Jarvis

+0

@ Jarvisいくつかのコンパイラは、これら2つのステートメントに対して同じ答えを返すかもしれません。 – yeputons

+0

ここにあいまいさがあるのはなぜですか?なぜそれは修正されていないのですか? – Jarvis

1

原因はf()関数がそのxパラメータの副作用を有することです。このパラメータに渡される変数は、関数が戻るときに第2パラメータcの値だけ増分されます。あなたは、オペランドの順序を入れ替えるときxは、関数が呼び出される前と後の異なる値が含まれているよう

はそのため、あなたは異なる結果が得られます。

ただし、このように記述されたコードの動作は、コンパイラが任意の順序でオペランドの評価を自由に入れ替えることができるため、未定義であることに注意してください。したがって、さまざまなプラットフォーム、コンパイラ、またはさまざまな最適化設定でも異なる動作をすることができます。そのため、一般にそのような副作用を避ける必要があります。詳細については、http://en.cppreference.com/w/c/language/eval_order