2009-03-07 9 views
52

オーケーでの評価の引数の順序は、私は、標準のC++の実装は、関数の順序の引数が評価される選ぶことができるが、これを実際には「を活用する」任意の実装があることを指示することを承知しています実際にプログラムに影響を与えるシナリオでは?コンパイラとC++

クラシック例:

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

注:私は評価の順序をあてにできないことを、私はそれをよく知ってる私に教えてくれる人を探していませんよ。私は、コンパイラが左から右への順序から実際に評価するかどうかという点にのみ関心があります。なぜなら、あまり書かれていないコードの多くが壊れてしまえば(正しくはそうだが、おそらく不平を言っているだろう)と思うからです。

+0

別のコンパイラで自分で試してみてください。 – strager

+0

C++のサブセットのインタプリタを実装しているときに同じ質問をしました。 Phew。 –

+6

'foo(i ++、i ++)'は未定義のビヘイビアを呼び出すことに注意してください。なぜなら、 'i'は1つ以上のシーケンスポイントを介することなく1つずつインクリメントされるからです。 – Nawaz

答えて

49

これは、引数の型、呼び出された関数の呼び出し規約、archtectureやコンパイラに依存します。 x86では、Pascal呼び出し規約はC呼び出し規約(__cdecl)で、それは右から左にあるのに対し、引数は左から右に評価されます。複数のプラットフォームで動作するほとんどのプログラムは、驚きをスキップするために呼び出し規約を考慮しています。興味のある方は

はレイモンド陳氏のブログに素敵なarticleがあります。また、GCCマニュアルのStack and Callingセクションをご覧ください。

編集:私たちは髪を分割している限り、私の答えはこれを言語の問題ではなくプラットフォームとして扱います。言語規格は、他のものよりも優位性を持たないか、どちらか一方を優先し、と記載されていませんです。言葉に注意してください。これは定義されていないとは言いません。この意味では、あなたが頼りにできない、ポータブルでない行動を意味します。 (わたしは便利なC仕様/ドラフトを持っていないが、それは抽象マシンの

いくつかの他の態様および操作が指定されていないとして、この規格に記述されている私のn2798ドラフト(C++)からのものと同様でなければなりません例、関数への引数の評価の順序)。可能であれば、この国際規格は一連の許容される行動を定義する。これらは抽象機械の非決定論的側面を定義する。したがって、抽象機械のインスタンスは、所与のプログラムおよび所与の入力に対して2つ以上の可能な実行シーケンスを有することができる。

+11

pascal/cdeclは引数が左から右に渡されるか右から左に渡されるかを決定します。これらの引数は任意の順序で*評価することができます。 (レジスタの圧迫により、渡されたのと同じ順序で評価される可能性があります) –

+8

間違っています:Cの引数が右から左のスタックにプッシュされます。しかし、評価される順番は不明です。 –

+1

@Martin York:注文は明確にいつ定義されたのですか? – dirkgently

1

私は違いが見た最後の時間は、だから、(だった?)可能性が高い状況だ2007年 にx86ハードウェア上でVS2005とGCC 3.xの間でした。だから私はもう評価オーダーに頼ることはありません。多分今は良いです。

+0

ああ?それは知っていることはとても良いことです。ありがとう。 現在私が使用している唯一のコンパイラはVC 08とICC 11です。おそらくGCCで自分のコードのテストを開始するべきでしょう。 – RaptorFactor

+2

ICCは、できるだけVSと互換性があるようにすばらしい長さになります。それらのインテルの人々良い人: –

+0

VSは評価の順序を保証しません。彼らは以前に関数の引数をどのように評価したかを変更しました。あなたが頼りにするべきことではありません。 – jalf

2

は、私が最も最近のコンパイラは、それらが独立しているようにC++標準で必要とされるので、任意の相互依存性が欠如していることを考えると、引数を計算指示をインターリーブしようとすることを期待しています。これを行うと、深くパイプライン化されたCPUの実行単位がいっぱいになるので、スループットが向上します。 (少なくとも私は、最適化のフラグが指定されているとき、最適化コンパイラであることを主張するコンパイラがそうであろうと予想されます。)

6

Read this

それはあなたの質問の正確なコピーではないのですが、私の答え(およびAいくつかの他の)あなたの質問を同様にカバーします。

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

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

はい、私はGCCのいくつかのバージョンがまさにこれをしているのを見ました。あなたの例では、foo(0,0)が呼び出され、後で2になります。 (私はあなたにコンパイラの正確なバージョン番号を与えることはできません。それはもう少し前でした - しかし、私はこの挙動が再びポップアップするのを見て驚くことはありません)

+0

共通部分式の削除は、最適化の再順序付けの主な理由です。引数の評価とパラメータの受け渡しは、多くのコンパイラで同じように扱われますが、異なることが許されます。 – plinth

3

すべての引数評価される。注文は定義されていません(標準に従って)。しかし、私が知っているC/C++のすべての実装では、関数の引数をから右へまで評価しています。 EDIT:CLangは例外です(下記のコメントを参照)。

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

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

右から左へのプッシュ順では、スタックポインタの直下のスタックスロットは常に第1引数を保持し、次のスロットは第2引数などを保持します。引数オフセットは常に関数呼び出された場所とは別に、別の場所でライブラリに書き込まれ、コンパイルされてもよい)。

右から左へのプッシュオーダーでは右から左への評価順序は必須ではありませんが、初期のコンパイラではメモリが不足しています。右から左への評価順序では、同じスタックをインプレースで使用することができます(基本的には、引数を評価した後、式やファンクションコールとなる可能性があります)。戻り値はすでにスタック)。左から右の評価では、引数の値を別々に格納し、逆の順序でスタックにプッシュバックする必要があります。

+0

clangは、左から右の評価を使用します。 – Kaiserludi

8

回答はc++ standardsで見つかりました。

段落5.2.2.8:

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

つまり、コンパイラによってのみ異なります。

+2

ええ、質問には反応しません。 – ThomasMcLeod