60

さて、標準ではC ++実装が関数の引数を評価する順序を選択できることを知っていますが、実際にプログラムに影響を与えるシナリオでこれを実際に「利用」する実装はありますか?

古典的な例:

int i = 0;
foo(i++, i++);

注:評価の順序は信頼できないと言ってくれる人を探しているわけではありません。私はそれをよく知っています。私が興味を持っているのは、コンパイラーが実際に左から右の順序で評価するかどうかだけです。なぜなら、コンパイラーが多くの不適切に記述されたコードを実行すると、壊れてしまうからです(もちろんそうですが、おそらく文句を言うでしょう)。

4

6 に答える 6

54

これは、引数の型、呼び出された関数の呼び出し規約、アーキテクチャ、およびコンパイラによって異なります。x86では、Pascal呼び出し規約は引数を左から右に評価しますが、C呼び出し規約(__cdecl)では右から左になります。複数のプラットフォームで実行されるほとんどのプログラムは、驚きをスキップするために呼び出し規約を考慮に入れています。

興味があれば、レイモンド・チェンのブログに素敵な記事があります。また、GCCマニュアルの「スタックと呼び出し」セクションも参照してください。

編集:私たちが髪を分けている限り:私の答えはこれを言語の質問としてではなく、プラットフォームの質問として扱います。言語標準は、一方を他方よりも保証または優先することはなく、指定されていないままにします。言葉遣いに注意してください。これが未定義であるとは言いません。この意味での不特定とは、信頼できない、移植性のない動作を意味します。私はCスペック/ドラフトを手元に持っていませんが、それは私のn2798ドラフト(C ++)のものと似ているはずです

抽象マシンのその他の特定の側面と操作は、この国際規格では指定されていないものとして説明されています(たとえば、関数への引数の評価の順序)。可能な場合、この国際規格は一連の許容される動作を定義します。これらは、抽象マシンの非決定論的側面を定義します。したがって、抽象マシンのインスタンスは、特定のプログラムおよび特定の入力に対して複数の可能な実行シーケンスを持つことができます。

于 2009-03-07T08:48:37.947 に答える
7

私はc++ 標準で答えを見つけました。

パラグラフ 5.2.2.8:

引数の評価順序は規定されていません。引数式の評価のすべての副作用は、関数に入る前に有効になります。後置式と引数式リストの評価順序は規定されていません。

つまり、コンパイラのみに依存します。

于 2012-01-09T08:15:44.420 に答える
6

すべての引数が評価されます。順序が定義されていません (標準による)。しかし、(私が知っている) C/C++ のすべての実装は、関数の引数をright から left に評価します。 編集: CLang は例外です (以下のコメントを参照)。

右から左への評価順序は非常に古いものだと思います (最初の C コンパイラ以来)。確かに、C++ が発明されるずっと前に、初期の C++ 実装は単純に C に変換されたため、C++ のほとんどの実装は同じ評価順序を維持していました。

関数の引数を右から左に評価する技術的な理由がいくつかあります。スタック アーキテクチャでは、通常、引数はスタックにプッシュされます。C/C++ では、実際に指定されているよりも多くの引数を指定して関数を呼び出すことができます。余分な引数は単純に無視されます。引数が左から右に評価され、左から右にプッシュされる場合、スタック ポインターのすぐ下のスタック スロットが最後の引数を保持し、関数が特定の引数のオフセットに到達する方法がありません。 (実際にプッシュされる引数の数は呼び出し元によって異なるため)。

右から左へのプッシュ順序では、スタック ポインターのすぐ下のスタック スロットは常に最初の引数を保持し、次のスロットは 2 番目の引数を保持します。呼び出された場所とは別に、別の場所でライブラリにコンパイルされます)。

現在、右から左へのプッシュ順序は右から左への評価順序を強制するものではありませんが、初期のコンパイラではメモリが不足しています。右から左への評価順序では、同じスタックをその場で使用できます (基本的に、引数を評価した後 (式または関数呼び出しである可能性があります!)、戻り値は既に正しい位置にあります)。スタック)。左から右への評価では、引数の値を個別に格納し、逆の順序でスタックにプッシュ バックする必要があります。

于 2011-03-22T12:10:50.000 に答える
6

これを読む

それはあなたの質問の正確なコピーではありませんが、私の答え(および他のいくつか)はあなたの質問もカバーしています.

コンパイラが右から左を選択するだけでなく、それらをインターリーブするのには、非常に適切な最適化の理由があります。

標準では、順序付けも保証されていません。関数が呼び出されたときに、すべての引数が完全に評価されていることを保証するだけです。

はい、GCC のいくつかのバージョンがまさにこれを行うのを見てきました。あなたの例では、 foo(0,0) が呼び出され、その後 i は 2 になります。(コンパイラの正確なバージョン番号をお伝えすることはできません。それは少し前のことですが、この動作が再び表示されても驚かないでしょう。命令をスケジュールする効率的な方法です)

于 2009-03-07T10:57:03.550 に答える
2

前回、違いを見たのは、2007年のx86ハードウェア上のVS2005とGCC 3.xの違いでした。したがって、それは(以前は?)非常にありそうな状況です。だから私はもう評価順序に頼ることはありません。多分それは今より良いです。

于 2009-03-07T08:52:07.470 に答える
2

最近のコンパイラのほとんどは、C ++標準で独立している必要があり、相互依存性がないことを考えると、引数を計算する命令をインターリーブしようとすると思います。これを行うと、深くパイプライン化されたCPUの実行ユニットがいっぱいになり、スループットが向上するはずです。(少なくとも、最適化フラグが与えられたときに、最適化コンパイラーであると主張するコンパイラーがそうすることを期待します。)

于 2009-03-07T08:54:27.713 に答える