一時保存なしで2つの値を切り替える

私はこのコードを書いて、2つの値を一時的な記憶容量なしで切り替えることができますが、それはうまく機能しますが、同じスタイルを使って他のことをしようとすると、

                `     
 #include 
 using namespace std;

 int main(){
   int i=100,j=200;
   //expect swich i and j
   i=j+i-(j=i);
   cout<

結果は次のとおりです。

200    100
400    200

2番目の(j = i)はなぜ機能しないのですか?

このような状況について議論することはできますか?私を含め誰もこのようなコードを維持することはありません。

1
そのようなコードは、いかなる言語でも書かれるべきではありません。メンテナンス性について考えてみましょう - 誰がこれを3ヶ月で理解しようとしていますか?あなたが実際に何をしているのかを知るには、おそらく数週間で数分かかるでしょう。
追加された 著者 Jan Thomä,
これはちょうど楽しみのための権利ですか?あなたは本当にこれを使用する予定ですか?
追加された 著者 David Heffernan,
これを実際に安全にしたい場合は、 XORスワップをご覧ください。
追加された 著者 juanchopanza,
オフトピックコメント。あなたが最初にそれを書いたときから数週間それを見たときのようなコードは、メンテナンスの悪夢です。私はもっ​​と慣れ親しんだものを選んだ。また、 i j の値が最大許容値に近い場合、ソリューションがフレークになることがあります。
追加された 著者 Noufal Ibrahim,
また、C ++ 11ではこれが定義されている方法が変更されます。 C ++ 11の用語では、組み込みの + の2つのオペランドは順序付けされておらず、UBは順序付けられていない j (代入)その価値の使用。
追加された 著者 Steve Jessop,
未定義の動作とシーケンスポイントの可能な複製。特に、 j の新しい値を決定する以外に j が変更され、アクセスされます。
追加された 著者 Steve Jessop,

6 答え

これは未定義の動作(UB)です。 +へのオペランドは、任意の順序で評価できます。あなたのコードの意図は、選択された特定の順序に依存しますが、コンパイラは他の評価順序を選択しています。あなたのために不運です。

このような経験は、あなたがこのようなコードを書くことを避けるように説得するのに十分であるはずです。式に副作用を引き起こすとすぐに、UBを避けるように注意する必要があります。スワップ時にローカルの一時変数を使用するのを恐れないでください。また、標準ライブラリに組み込まれているスワップルーチンを使用することをあまり恐れることはありません。

最後の一点。変数を宣言しないという事実は、生成されたコードが変数を使用しないことを意味するものではありません。それはレジスタ内にあるかもしれませんが、コンパイラがあなたのtemp intをスワップ関数のどこに置くと思いますか?

8
追加された

XORのスワップを使用することができます:

i ^ = j; j ^ = i; i ^ = j;

しかし、今日のプロセッサでは偽の経済です。

5
追加された
@DavidHefernan:「しかし、今日のプロセッサでは間違っている。」私はこの文章から理解しました。申し訳ありませんが、ネイティブの英語話者ではありません。
追加された 著者 the_drow,
今日のプロセッサではうまくいかない理由を説明できますか?私はPythonでそれを実行し、それは動作しました。
追加された 著者 the_drow,
@SteveJessopあなたはXORの交換を使用して失うものを説明できますか?私の理解では、一時変数を持つものより少し遅いですが、それはいくつかのスペースを節約するかもしれません。
追加された 著者 lastland,
@the_drowは動作します。それはうまくいかないと答えていません。答えは、xor swapを使用するためのパフォーマンス上の理由がないということだけです。
追加された 著者 David Heffernan,
@the_drow:「虚偽の経済」は、経済的である(この場合、変数を保存するように見える)が、実際にはそれよりも多くまたはそれ以上を失うことを意味するイディオムである(この場合、今日のプロセッサコンパイラは、コードの高速化という大きな可能性はありません)。
追加された 著者 Steve Jessop,

それを与えると、あなたの "メソッド"は未定義の振る舞いをします。

前後のシーケンスポイントの間にオブジェクトは   記憶された値は、   表現。さらに、前の値は   格納する値を決定します。

具体的に言えば、 y の評価によって古い値や新しい値が得られるという保証はありません。コンパイラはそれらのいずれかを選ぶことができます。

理論的に定義されていない動作は、コードがハードドライブを食べたり、すべてのお金を費やしたりする可能性があることを意味します。

これは疑わしい最適化であり、コードを読むことができなくなります。そんなふうにしないでください。

4
追加された

おそらく、評価の順序についてのあなたの仮定は間違っているでしょう。

i=j+(j=i);

IIRCでは、これは指定されていません(C ++ではこれについて確信しています、C ++標準§1.9(15)を参照)。

j (等号の後)が評価される前に(j = i)が評価された場合、結果は異なる場合とは異なります。

要するに、C ++を使用している場合は std :: swap()してください。これにより、あなたの意図が明確に示されます(標準ライブラリの実装者が最適化を使用できるようになります)。

2
追加された

算術演算でオーバーフローすることがあります。 ビットスワップアルゴリズムもあります:

a = a^b
b = a^b
a = a^b
1
追加された
:)ニース!しかし、通常彼らはインタビュー中にライブラリを使用しないように頼んでいます:)
追加された 著者 Eugen Martynov,
あなたのコードを `std :: swap(a、b); 'と比較し、自分自身をイメージングしてメンテナンスプログラマーにしてください。 ;-)
追加された 著者 Andre,

次のようなものを試してください:

a = a + b;

b = a-b;

a = a-b;

1
追加された
@GPSingh: a + b の値が式のデータ型(この場合は int )の最大値を超えるとしますか?
追加された 著者 Nawaz,
a + b にオーバーフローがある場合はどうなりますか?
追加された 著者 Nawaz,
のような a + b のオーバーフローは、 a 型の変数の最大値を超えています。
追加された 著者 Noufal Ibrahim,
あなたのメソッドはすべてのケースでオーバーフローすることはありませんが、一時的なスワップを使用すると決してオーバーフローしません。
追加された 著者 Blastfurnace,
しかし、問題の方法は一時変数なしでスワップすることでした。
追加された 著者 gprathour,
これは、このメソッドを使用する際の制限の1つです。しかし、私はそれがすべての場合に起こるとは思わないと思う。
追加された 著者 gprathour,
オーバーフロー?のように?
追加された 著者 gprathour,