int c = 0の結果。 cout << c ++ << c;
質問
私はそれが01であるべきだと思いますが、誰かがその「未定義」と言っています、それの理由は何ですか?
解決
c++
インクリメントと割り当ての両方です。割り当てが発生した場合(その行の他のコードの前または後)、コンパイラの裁量に任されます。後に発生する可能性があります cout <<
または前。
これは、C99標準にあります http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
28ページでPDFまたはセクション5.1.2.3で見つけることができます
Pの実際の増分は、前のシーケンスポイントと次のシーケンスポイントの間にいつでも発生する可能性があります
誰かがC ++標準を要求したため(これはC ++の質問であるため)、セクション1.9.15ページ(またはPDF形式で24)に記載されています。
個々のオペレーターの手術と個々の表現のサブエクスペリションの評価は無効になっています
また、次のコードブロックも含まれています。
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
C99 Standardの説明は明確であると感じていますが、両方の言語で当てはまります。
他のヒント
介在するシーケンスポイントなしで値を変更してから読み取る(またはもう一度変更しようとする)場合は、未定義の動作です。 C ++のシーケンスポイントの概念は少し技術的です(あなたはそれについて少し読むことができます ここ)、しかし、一番下の行はそのストリーム挿入です(<<
) は いいえ シーケンスポイント。
これが未定義の動作である理由は、シーケンスポイントがない場合、コンパイラが適切と思われる方法で操作を再注文できるためです。つまり、の値を取得することが許可されています c
(そして、2回目の挿入のためにそれを保持します)そして、その後の後語を実行します c++
最初の挿入の値を取得します。したがって、の増分がの前または後に発生するかどうかはわかりません c
2番目の挿入については決定されます。
それが未定義の理由は、コンパイラが任意の順序で関数パラメーターを自由に計算できるためです。関数を呼び出す場所を検討してください(あなたがそうであるためですが、機能構文があるときに想像するのは簡単です):
cout.output(c++).output(c);
コンパイラは、パラメーターを逆順序、フォワードオーダーなどでヒットする場合があります。パラメーターを2番目の出力に計算する前に、最初の出力を呼び出すか、両方を実行してから呼び出すことがあります。
動作は定義されていますが、特定されていません。式で「C」の2つの使用を評価する相対的な順序は指定されていません。ただし、機能表記に変換すると、次のようになります。
cout.operator<<(c++).operator<<(c);
関数に対する引数を評価することと、関数の本体を実行することと、関数本体の実行の間にはシーケンスポイントがあります。そのため、結果は未定義の動作ではなく、不特定のみです。
オーバーロードされた演算子がいなかった場合:
int c=0;
int a = c++ << c;
その場合、動作は未定義になります。 c
介在するシーケンスポイントなし。
編集:シーケンス litb
もたらすのは単に間違っています。標準は(§1.9/17)を指定します。関数本文の声明。」
これは、議論が評価されるという考えで明確に書かれており、(その後)関数の本体が実行されます。彼が提案するシーケンスは、ある関数に対する引数が評価され、次に別の関数への引数、次に両方の関数体の実行が意図されていなかったようですが、禁止されていないようです。しかし、それは何も変わりません - 要件はまだ次のことです。「...すべての関数引数の評価後(もしあれば)シーケンスポイントがあります...」
身体の実行に関する後続の言語は、すべての関数引数を評価した後、シーケンスポイントの要件を削除しません。 全て その他の評価、関数本体または他の関数引数がそのシーケンスポイントに従うかどうか。私は、明確に意図されていることを誤って読むことについて誰と同じようにペダンティックでひねくろすることができます(しかしそうではありません とても - しかし、「すべての関数引数の評価後にシーケンスポイントがある」とは、「すべての関数引数の評価後にシーケンスポイントがない」と読むことができると想像することはできません。
ニールのポイントは、もちろん正しいことです。上記で使用した構文は、メンバー関数用です。非会員の過負荷の場合、構文は次のようになります。
operator<<(operator<<(cout,c++), c);
ただし、シーケンスポイントの要件も削除されません。
不特定の限り:それは非常に簡単です。すべての関数引数を評価した後のシーケンスポイントがあります。したがって、1つの関数呼び出しのすべての引数を完全に評価する必要があります(すべての副作用を含む)。評価された(他者からの副作用を考慮して) - しかし、どの関数のコールの引数を最初または2番目に評価する必要があるかについては、どの機能を評価する必要があるかについては要求はありません。 c
, 、 それから c++
, 、またはそうかもしれません c++
, 、 それから c
- しかし、それはインターリービングではなく、どちらか一方でなければなりません。
私が見るように、f(c ++); F(C)に相当します。 C += 1;
およびf(c ++、c ++);と同等です:f(c、c); C += 1; C += 1;
しかし、f(c ++、c ++)の場合かもしれません。 f(c、c+1)になります。 C+= 2;
最初にCで、GCCとClangを使用した実験
#include <stdio.h>
void f(int a, int b) {
printf("%d %d\n",a,b);
}
int main(int argc, char **argv) {
int c = 0;
f(c++,c++);
return 0;
}
およびC ++で
#include <iostream>
int main(int argc, char **argv) {
int c = 0;
std::cout << c++ << " " << c++ << std::endl;
return 0;
}
GCCとG ++がコンパイルしたため、1 0でコンパイルされたため、Clangは0 1でコンパイルされたため、興味深いです