シーケンスポイントとは何ですか? 未定義の動作とどのように関係しますか? 質問する

シーケンスポイントとは何ですか? 未定義の動作とどのように関係しますか? 質問する

「シーケンスポイント」とは何ですか?

未定義の動作とシーケンス ポイントの関係は何ですか?

a[++i] = i;私は気分を良くするために、のようなおかしくて複雑な表現をよく使います。なぜそれらを使うのをやめるべきなのでしょうか?

これを読んだら、必ずフォローアップの質問をご覧ください未定義の動作とシーケンスポイントが再ロードされました


(注:これはStack Overflow の C++ FAQこの形式でFAQを提供するというアイデアを批判したい場合は、このすべてを始めたのはメタへの投稿だったそれをする場所になるでしょう。その質問への回答は、C++ チャットルーム、FAQ のアイデアが最初に生まれた場所なので、あなたの回答はそのアイデアを思いついた人たちに読まれる可能性が非常に高くなります。

ベストアンサー1

C++98 および C++03

この回答は、C++ 標準の古いバージョンに関するものです。標準の C++11 および C++14 バージョンには、正式には「シーケンス ポイント」が含まれていません。代わりに、操作は「前にシーケンスされる」または「シーケンスされない」または「不確定にシーケンスされる」ことになります。実質的な効果は基本的に同じですが、用語が異なります。


免責事項:TL'DR。

前提条件:基礎的な知識C++ 標準


シーケンスポイントとは何ですか?

スタンダードは言う

実行シーケンス内の特定のポイント(シーケンスポイントと呼ばれる)では、以前の評価のすべての副作用が完了し、後続の評価の副作用は発生していない必要があります。(§1.9/7)

副作用?副作用とは何ですか?

式を評価すると何かが生成され、さらに実行環境の状態が変化する場合は、式 (の評価) に何らかの副作用があると言われます。

例えば:

int x = y++; //where y is also an int

初期化操作に加えて、演算子yの副作用により の値が変更されます。++

ここまでは順調です。シーケンス ポイントに移ります。comp.lang.c の作者によって与えられたシーケンス ポイントの代替定義Steve Summit:

シーケンス ポイントは、事態が落ち着き、これまでに確認されたすべての副作用が完了したことが保証される時点です。


C++ 標準に記載されている共通シーケンス ポイントとは何ですか?

それらは:

  • 完全な式()の評価の最後に§1.9/16(完全な式とは、別の式の部分式ではない式のことです。)1

    例 :

    int a = 5; // ; is a sequence point here
    
  • §1.9/18最初の式( )2の評価後の次の各式の評価において

    • a && b (§5.14)
    • a || b (§5.15)
    • a ? b : c (§5.16)
    • a , b (§5.18)(ここで、 a 、 b はカンマ演算子です。 はfunc(a,a++) ,カンマ演算子ではなく、単に引数aとの間の区切り文字ですa++。したがって、その場合の動作は未定義です ( がaプリミティブ型と見なされる場合))
  • 関数呼び出し時(関数がインラインであるかどうかに関係なく)、関数本体内の式または文の実行前に行われるすべての関数引数(存在する場合)の評価後(§1.9/17)。

1 : 注: 完全な式の評価には、完全な式の語彙的部分ではない部分式の評価が含まれる場合があります。たとえば、デフォルト引数式 (8.3.6) の評価に関係する部分式は、デフォルト引数を定義する式ではなく、関数を呼び出す式で作成されるものとみなされます。

2 : 示されている演算子は、条項 5 で説明されている組み込み演算子です。これらの演算子の 1 つが有効なコンテキストでオーバーロードされ (条項 13)、ユーザー定義の演算子関数が指定されると、式は関数呼び出しを指定し、オペランドは引数リストを形成し、それらの間に暗黙のシーケンス ポイントはありません。


未定義の動作とは何ですか?

この規格では、セクションの未定義の動作を§1.3.12次のように定義しています。

例えば、誤ったプログラム構造や誤ったデータの使用によって生じる可能性のある動作については、この国際規格では要件を課していません3

この国際規格で動作の明示的な定義の記述が省略されている場合にも、未定義の動作が予想される場合があります。

3 : 許容される未定義の動作は、状況を完全に無視して予測できない結果をもたらすものから、変換中またはプログラム実行中に環境の特性として文書化された方法で動作すること (診断メッセージの発行の有無にかかわらず)、変換または実行を終了すること (診断メッセージの発行あり) までの範囲にわたります。


未定義の動作とシーケンス ポイントの関係は何ですか?

その前に、次の違いを知っておく必要があります。未定義の動作、未指定の動作、実装定義の動作

あなたもそれを知っておく必要がありますthe order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified

例えば:

int x = 5, y = 6;

int z = x++ + y++; //it is unspecified whether x++ or y++ will be evaluated first.

もう一つの例ここ


今の標準で§5/4

    1. 前のシーケンス ポイントと次のシーケンス ポイントの間では、スカラー オブジェクトに格納された値は、式の評価によって最大 1 回変更されます。

それはどういう意味ですか?

非公式には、2 つのシーケンス ポイント間で変数を複数回変更してはならないことを意味します。式ステートメントでは、 はnext sequence point通常、終了セミコロンにあり、 はprevious sequence point前のステートメントの末尾にあります。式には中間の が含まれる場合もありますsequence points

上記の文から、次の式は未定義の動作を呼び出します。

i++ * ++i;   // UB, i is modified more than once btw two SPs
i = ++i;     // UB, same as above
++i = 2;     // UB, same as above
i = ++i + 1; // UB, same as above
++++++i;     // UB, parsed as (++(++(++i)))

i = (i, ++i, ++i); // UB, there's no SP between `++i` (right most) and assignment to `i` (`i` is modified more than once btw two SPs)

ただし、次の表現は問題ありません。

i = (i, ++i, 1) + 1; // well defined (AFAIK)
i = (++i, i++, i);   // well defined 
int j = i;
j = (++i, i++, j*i); // well defined

    1. さらに、以前の値は、保存する値を決定するためにのみアクセスされます。

それはどういう意味でしょうか? オブジェクトが完全な式内で書き込まれる場合、同じ式内でのそのオブジェクトへのすべてのアクセスは、書き込まれる値の計算に直接関与する必要があることを意味します。

たとえば、(LHS と RHS の)i = i + 1すべてのアクセスは、書き込まれる値の計算に直接関係しています。したがって、問題ありません。i

このルールは、アクセスが明らかに変更に先行するものに法的表現を効果的に制限します。

例1:

std::printf("%d %d", i,++i); // invokes Undefined Behaviour because of Rule no 2

例2:

a[i] = i++ // or a[++i] = i or a[i++] = ++i etc

は、 のアクセスの 1 つi( 内のアクセスa[i]) が、 i に格納される値 ( 内で発生するi++) とは何の関係もないため、許可されません。そのため、アクセスが増分値が格納される前か後かを定義する適切な方法 (私たちの理解でもコンパイラーの理解でも) がありません。したがって、動作は未定義です。

例3:

int x = i + i++ ;// Similar to above

C++11 のフォローアップ回答ここ

おすすめ記事