2011-01-16 24 views
156

次のforループは、ポストインクリメントと他のプリインクリメントを使用しても同じ結果をもたらします。ここで'for'ループ内のポストインクリメントとプリインクリメントは同じ出力を生成します

は、コードは次のとおりです。

for(i=0; i<5; i++) { 
    printf("%d", i); 
} 

for(i=0; i<5; ++i) { 
    printf("%d", i); 
} 

私はループ「の」両方に同じ出力を得ます。何か不足していますか?

答えて

267

i++または++iを評価した後、新しい値iはどちらの場合も同じになります。プリインクリメントとポストインクリメントの違いは、式自体を評価した結果です。

++i増分iとなり、新しい値はiと評価されます。

i++は、古い値がiであり、増分がiとなります。

これはループのために重要ではありません。その理由は、制御の流れは、おおよそ次のように動作することである:それがfalseの場合、それがある場合

  1. テスト条件
  2. は、
  3. を終了真、(1)ので増分ステップ

を実行し、(4)のいずれかの前または後INCR、切り離される

  • 体を実行使用することができます。

  • +7

    増分がクラスに呼び出されたときの動作はあまり異なる場合がありますから、それは、非同等ポストまたはプリインクリメントを呼び出すことになるかもしれません。 – mbaitoff

    +0

    'i ++'と '++ i'の間に構文的な違いがない場合、なぜ' ++ i'を使う人が見えますか?フードの下であっても、*相違はありますか? –

    +51

    'i ++'がインクリメント後に 'i'の古い値を記憶する必要があることを考えれば、' ++ i'はもっと短くてもよい(1-2命令のオーダー)。 – danben

    1

    はい、両方の出力がまったく同じになります。なぜ彼らはあなたに異なる出力を与えるべきだと思いますか?

    ポストインクリメントまたはこのような状況では、プリインクリメント事項:あなたが割り当てることによって、または引数を渡すことで、どちらかの、いくつかの価値を提供

    int j = ++i; 
    int k = i++; 
    f(i++); 
    g(++i); 
    

    forループではどちらも行いません。それはインクリメントされるだけです。ポストとプレは意味がありません!

    5

    どちらの場合も、増分はループの本体の後に行われるため、ループの計算には影響しません。コンパイラが愚かであれば、ポストインクリメントを使用するほうが効率的ではないかもしれません(後で使用するために通常はの前のの値のコピーを保持する必要があるため)。この場合、 。

    どのようにforループが実装され、基本的に割り当て、テスト、および分岐命令のセットに変換されるか考えるのは便利かもしれません。擬似コードでは、プリインクリメントは次のようになります。

     set i = 0 
    test: if i >= 5 goto done 
         call printf,"%d",i 
         set i = i + 1 
         goto test 
    done: nop 
    

    ポストインクリメントは、少なくとも別のステップを持っているでしょうが、離れて

     set i = 0 
    test: if i >= 5 goto done 
         call printf,"%d",i 
         set j = i // store value of i for later increment 
         set i = j + 1 // oops, we're incrementing right-away 
         goto test 
    done: nop 
    
    1

    私は++と++私も最適化するために、些細なことだろうprintf( "%d"、i)が毎回実行された後に実行されるため、違いはありません。

    100

    これは簡単です。上記forループは、線i++;++i;は、コードブロックの観点から同じ意味を持っていることを注意

    int i = 0; 
    while(i < 5) { 
        printf("%d", i); 
        i++; 
    } 
    

    int i = 0; 
    while(i < 5) { 
        printf("%d", i); 
        ++i; 
    } 
    

    と意味的に等価です。両方とも、iの値に同じ効果を持ちます(1つ増分する)ので、これらのループの動作にも同じ効果があります。ループはこのコードjの最初のブロックにインクリメント後iの値を見ているからである(iを最初にインクリメント、またはプレさ

    int i = 0; 
    int j = i; 
    while(j < 5) { 
        printf("%d", i); 
        j = ++i; 
    } 
    
    int i = 0; 
    int j = i; 
    while(j < 5) { 
        printf("%d", i); 
        j = i++; 
    } 
    

    ように書き換えた場合に差が存在するであろうこと

    注インクリメンタルなので、名前)と、コードの第2ブロックjは、インクリメントの前にiの値を見ます。

    +2

    別のものが発生する可能性があることを説明するニースの答え! Upvote :) –

    -2

    コンパイラは、あなたのケース(ポスト/プリインクリメント)中のSO

    a; 
    while(b) 
    { 
        ... 
    end: 
        c; 
    } 
    

    for (a; b; c) 
    { 
        ... 
    } 
    

    を翻訳し、それは問題ではありません。

    EDITは:単純にこれは私のお気に入りのインタビューの質問の一つであるgoto end;

    +0

    実際には、それはかなり正しくはありません。 'for(int i = 0; i <42; i ++){printf("%d "、i);}を見てください。持続する; } '例えば。あなたの主張は、それは意味的に 'int i = 0;と同じです。 while(i <42){printf( "%d"、i);持続する;私は+ +; } 'それは明らかに間違っています。 – jason

    +0

    私の編集を参照してください。この声明は私のものではなく、私がよく覚えていればコンパイラについて読んだ本からです。この本はhttp://www.amazon.com/Compilers-Principles-Techniques-Alfred-Aho/dp/0201100886でした。 – jdehaan

    +1

    私はそれがドラゴンの本のような間違いを疑う。いずれにしても、もう少し注意深い読みが順調でしょうか? – jason

    23

    に置き換えられ続けます。最初に答えを説明し、なぜ私は質問が好きなのかを説明します。

    ソリューション:

    答えが包括的、両方のスニペットは、0から4までの数字を印刷しておくことです。 for()ループは、一般的にwhile()ループに相当しますので、これは次のとおりです。

    for (INITIALIZER; CONDITION; OPERATION) { 
        do_stuff(); 
    } 
    

    を書き込むことができます:

    INITIALIZER; 
    while(CONDITION) { 
        do_stuff(); 
        OPERATION; 
    } 
    

    あなたは操作がループの一番下で行わ常にであることがわかります。この形式では、i++++iが同じ効果を持つことが明らかです。これらは両方ともインクリメントiであり、結果は無視されます。 iの新しい値は、ループの先頭で次の反復が始まるまでテストされません。


    編集:ループがwhile()ループ内で実行されてからOPERATIONを防止するであろう(例えばcontinueなど)制御文が含まれている場合、このfor()while()に同値がないホールドをしていることを指摘してジェイソンのおかげで。OPERATIONは、for()ループの次の反復の直前に実行されると常にであるです。


    、それはすべての

    最初の質問良いインタビューだなぜ、候補者は、すぐに正しい答えを伝える場合にのみ2分かかりますので、私たちは次の質問に右に移動することができます。

    しかし、驚くべきことに、多くの候補者は、ポストインクリメントのループでは0から4までの数字が出力され、プリインクリメントループでは0から5、または1から5が出力されますプリインクリメントとポストインクリメントの違いを正しく説明していますが、ループのメカニズムを誤解しています。

    その場合、私はwhile()を使ってループを書き直すように頼んでいます。これは本当に私の思考プロセスの良いアイデアです。それで私は最初に質問をします。彼らの問題へのアプローチと、世界の動向に疑問を投げかけたときの進捗状況を知りたいのです。

    この時点で、ほとんどの候補者はエラーを認識し、正しい答えを見つけます。しかし、元の答えが正しかったと主張して、for()while()に翻訳した方法を変更しました。それは魅力的なインタビューのために作られましたが、私たちはオファーをしませんでした!

    希望に役立ちます!

    +16

    あなたは重大だが一般的な間違いをしたので、このインタビューの質問を再検討する必要があります。 'for'ループは、あなたが一般的に指定したように書き直すことはできません。 'for(int i = 0; i <42; i ++){printf("%d "、i);}を見てください。持続する; } '例えば。あなたの主張は、 'int i = 0;と意味的に同等であるということです。 while(i <42){printf( "%d"、i);持続する;私は+ +; } 'それは明らかに間違っています。 – jason

    +5

    @Jason:今日は、私が考慮していなかったエッジケースを学びました!ほぼ10年のうちに、私は候補者にこれを認識させることはなかった。私が言及した理由のために貴重なこの質問を引き続き使用するならば、私は上記の編集ごとにそれを言い換えてください。候補者が正解をすぐに提示した場合は、例外があるかどうかを尋ねます。あなたの明確な反例のために:-) +1。ありがとうございました! –

    +0

    @Jason、私は何かを紛失している、またはあなたの質問に対する回答が同じではない。 @アダム・リス、私はあなたの声明の中で正しいと思います、私の答えの補足を見てください。実際には、継続を非常に簡単に実装することもできます。 – jdehaan

    0

    場合に違いがあります:

    int main() 
    { 
        for(int i(0); i<2; printf("i = post increment in loop %d\n", i++)) 
        { 
        cout << "inside post incement = " << i << endl; 
        } 
    
    
        for(int i(0); i<2; printf("i = pre increment in loop %d\n",++i)) 
        { 
        cout << "inside pre incement = " << i << endl; 
        } 
    
        return 0; 
    } 
    

    結果:= 0

    ポストincement内部

    = 1

    ポストincement内部ループ0

    におけるI =ポストインクリメント

    i =ループ1のポストインクリメント

    0 forループ

    秒:予備incement内部

    = 0

    I =ループにおけるプリインクリメント1

    事前incement内部= 1

    I =ループにおけるプリインクリメント2

    +2

    この違いはforループとは無関係です。これは、ステートメントに副作用がある場合にプリ/ポストインクリメントを使用しているためです。 –

    1

    for構文の3番目の文は実行されますが、評価された値は破棄され、処理されません。
    評価値が破棄されると、前と後のインクリメントは等しくなります。
    値が異なる場合にのみ異なります。

    78

    コードの結果は同じになります。その理由は、2つのインクリメント操作が2つの別個の関数呼び出しであると見ることができるからです。どちらの関数も変数のインクリメントを引き起こし、戻り値のみが異なります。この場合、戻り値はただ破棄されます。つまり、出力には違いがありません。フード違いがあります下しかし

    からi++ニーズがiの元の値を格納する一時変数作成ポストインクリメントは、その後インクリメントを実行し、一時的な変数を返します。事前インクリメント++iは、一時変数を作成しません。確かに、オブジェクトがintのような単純なものであれば、適切な最適化設定はこれを最適化できるはずですが、++演算子はイテレータのような複雑なクラスでオーバーロードされることに注意してください。 2つのオーバーロードされたメソッドは異なる操作を持つ可能性があります(たとえば、 "Hey、I'll pre-incremented!"をstdoutに出力することがあります)、コンパイラは戻り値が使用されていないとき(基本的にこのようなコンパイラは解決不可能なhalting problemを解決するため)、myiterator++と書くと、より高価なポストインクリメントバージョンを使用する必要があります。あなたはインクリメントを事前すべき理由

    3つの理由:

    1. あなたは、変数/オブジェクトは、(テンプレート関数で、たとえば)オーバーロードされた後の増分方法を持っていると扱うかどうかを考える必要はありませんそれは異なっている(または異なって扱うことを忘れる)。
    2. 一貫性のあるコードがよく見えます。
    3. 誰かがあなたに「なぜ前にインクリメントするのですか?あなたは停止問題とtheoretical limits of compiler optimizationについて教える機会を得るでしょう。 :)
    +0

    私はあなたが間違っていると思います! ++ iとi ++の唯一の違いは、あなたが行うことの順序です。あなたは 株式会社*私は*私 を押し なり、他にあなたが プッシュ*私は 株式会社*私 私はこの最適化をconciderはないでしょう1の場合 。 –

    +14

    彼は間違っていません。ポストインクリメントプリミティブは、コピーを使用しないときにコンパイラによって非常に簡単に最適化されます。しかし、最後のビットで言及している点は、イテレータのポストインクリメントがメソッド呼び出しとイテレータの全く新しいコピーの構築として実装されていることです。コンストラクタと関数呼び出しは、コンパイラが追跡できない副作用を持つ可能性があるため、これらは重要ではないと想定できないため、コンパイラによって最適化することはできません。 – Jherico

    +0

    >あるケースでは私は* iを押し込み、もう1つは* i inc * iを押すでしょう。私はこれを最適化しないでしょう。 – user3697618

    4

    あなたはこのようにそれを書いた場合、問題になります。

    for(i=0; i<5; i=j++) { 
        printf("%d",i); 
    } 
    

    が複数回を繰り返すだろう。このように書かれている場合:あなたはそれのために、Googleの答えを読むことができた

    for(i=0; i<5; i=++j) { 
        printf("%d",i); 
    } 
    
    2

    ここでは: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Preincrement_and_Predecrement

    したがって、主な点は、単純なオブジェクトでは何の違いもありませんが、 rイテレータおよびその他のテンプレートオブジェクトを使用する必要があります。

    EDITED:あなたはループ本体の後に実行シンプルなタイプなので、副作用なし、と後またはpreincrements、ループ本体内の値にそれほど影響を与えずに使用しているため

    差はありません。

    あなたは、このようなループでそれをチェックすることができ:

    for (int i = 0; i < 5; cout << "we still not incremented here: " << i << endl, i++) 
    { 
        cout << "inside loop body: " << i << endl; 
    } 
    
    +0

    これは出力が同じ理由についての質問には答えません。 –

    +0

    @ボ・ペルソン、公平であるためには、私の答えだけでなく、そのような印を付けるべきです。私はあなたが本当に失礼だと思う。 – Yola

    +0

    私は多分、他の答えは2歳で、実際には答えてみる* "違いがない理由を知りたい" *。 –

    関連する問題