4

この投稿未定義の動作とシーケンス ポイントを使用して、 Cプログラムの未定義の動作 ( UB )を文書化しました。では、シーケンス ポイントと関連するUBに関して、 CC++の違いは何ですか? C++シーケンスに関する投稿を使用して、 Cコードで何が起こっているかを分析することはできませんか?C and C++ have their own divergent rules for this [sequence points]

C++* もちろん、 に当てはまらないの機能について話しているわけではありませんC

4

1 に答える 1

4

この質問には 2 つの部分があります。シーケンス ポイント ルールの比較に問題なく取り組むことができます。C と C++ は、異なる標準を持つ異なる言語です (最新の C++ 標準は、最新のC標準のほぼ 2 倍の大きさです)。C++ が C を規範的な参照として使用していても、それは正しくありません。特定のセクションがどれほど似ているかに関係なく、C の C++ 標準を引用し、その逆も同様です。C++ 標準は C 標準を明示的に参照していますが、それは小さなセクション用です。

2 番目の部分は、C と C++ の間の未定義の動作の比較です。いくつかの大きな違いがある可能性があり、未定義の動作のすべての違いを列挙することはできないかもしれませんが、いくつかの指標となる例を挙げることができます。

シーケンス ポイント

シーケンス ポイントについて話しているので、これは C++11 以前と C11 以前を対象としています。私が知る限り、C99 と Pre C++11 ドラフト標準の間で、シーケンス ポイント規則に大きな違いはありません。異なる未定義の動作のいくつかの例で説明するように、シーケンス ポイント規則はそれらの動作には関与しません。

シーケンス ポイントのルールは、 C++03セクションに最も近いドラフト C++ 標準でカバーされてい1.9 ます

  • 各完全式の評価が完了すると、シーケンスポイントがあります12)
  • 関数を呼び出すとき (関数がインラインであるかどうかにかかわらず)、すべての関数引数 (存在する場合) の評価の後に、関数本体の式またはステートメントの実行前に行われるシーケンス ポイントがあります。
  • また、戻り値をコピーした後、関数外の式を実行する前にもシーケンスポイントがあります13)。C++ のいくつかのコンテキストでは、対応する関数呼び出し構文が翻訳単位に表示されなくても、関数呼び出しが評価されます。[ 例: new 式の評価は、1 つ以上の割り当て関数とコンストラクター関数を呼び出します。5.3.4 を参照してください。別の例として、変換関数 (12.3.2) の呼び出しは、関数呼び出し構文が表示されないコンテキストで発生する可能性があります。—end example ] function-entry と function-exit のシーケンス ポイント (上記のとおり) は、関数を呼び出す式の構文が何であれ、評価される関数呼び出しの機能です。
  • それぞれの式の評価では

    a && b
    a || b
    a ? b : c
    a , b
    

    これらの式の演算子の組み込みの意味 (5.14、5.15、5.16、5.18) を使用すると、最初の式の評価の後にシーケンス ポイントがあります14)。

C99 標準の草案からのシーケンス ポイント リストを使用しますAnnex C。これは規範的ではありませんが、参照している規範的なセクションとの不一致を見つけることができません。それは言います:

以下は、5.1.2.3 で説明されているシーケンス ポイントです。

  • 引数が評価された後の関数の呼び出し (6.5.2.2)。
  • 次の演算子の最初のオペランドの終わり: 論理 AND && (6.5.13); 論理和 || (6.5.14); 条件付き?(6.5.15); コンマ、(6.5.17)。
  • 完全な宣言子の終わり: 宣言子 (6.7.5);
  • 完全な式の終わり: 初期化子 (6.7.8); 式文中の式(6.8.3); 選択ステートメントの制御式 (if または switch) (6.8.4); while または do ステートメントの制御式 (6.8.5)。for ステートメントの各式 (6.8.5.3); return ステートメントの式 (6.8.6.4)。

次のエントリは、ドラフト C++ 標準に相当するものはないようですが、これらは C++ が参照により組み込む C 標準ライブラリからのものです。

  • ライブラリ関数が戻る直前 (7.1.4)。
  • 各書式付き入出力関数変換指定子 (7.19.6、7.24.2) に関連付けられたアクションの後。
  • 比較関数の各呼び出しの直前と直後、および比較関数の呼び出しとその呼び出しに引数として渡されたオブジェクトの移動の間 (7.20.5)。

したがって、ここでは C と C++ の間に大きな違いはありません。

未定義の動作

シーケンス ポイントと未定義の動作の典型的な例について言えば、たとえば、シーケンス ポイント内で変数を 2 回以上変更するセクション5 で説明されているものなど、一方が未定義で他方が未定義である例を思いつくことはできません。 . C99では次のように書かれています:

前のシーケンス ポイントと次のシーケンス ポイントの間で、オブジェクトの格納値は、式の評価によって最大 1 回変更されます。72)さらに、以前の値は、保存する値を決定するためにのみ読み取られるものとします。73)

そして、次の例を提供します。

i = ++i + 1;
a[i++] = i;

そしてC ++では次のように書かれています:

特に明記されていない限り、個々の演算子のオペランドと個々の式の部分式の評価の順序、および副作用が発生する順序は規定されていません。式の評価によって最大 1 回。さらに、以前の値は、保存する値を決定するためにのみアクセスされます。この段落の要件は、完全な式の部分式の許容される順序ごとに満たされるものとします。それ以外の場合、動作は未定義です

次の例を示します。

i = v[i ++]; / / the behavior is undefined
i = ++ i + 1; / / the behavior is undefined

C++11 と C11 には、次のようなC11 式の代入演算子の順序付けで説明されている大きな違いが 1 つあります。

i = ++i + 1;

これは、プレインクリメントがC++11 では左辺値であるが、C11 では順序付け規則が同じであっても左辺値ではないためです。

シーケンス ポイントとは関係のない領域で大きな違いがあります。

おそらくもっと多くの例がありますが、これらは私が以前に書いたものです。

于 2014-07-23T02:19:35.400 に答える