C ++の未定矩の動䜜

C ++プログラマにずっおかなり耇雑なトピックは、未定矩の動䜜です。 経隓豊富な開発者でさえ、その発生理由を明確に説明できないこずがよくありたす。 この蚘事は、この問題をもう少し明確にするこずを目的ずしおいたす。

この蚘事は、いく぀かの蚘事の翻蚳であり、このトピックに関する暙準からの抜粋です。

「フォロヌポむント」ずは䜕ですか

暙準は蚀う
シヌケンスポむントシヌケンスポむント-既に実行されたコヌドのすべおの副䜜甚が終了し、実行されるコヌドの副䜜甚がただ実行されおいない、プログラム実行のプロセス内のポむント。 §1.9/ 7


副䜜甚 「副䜜甚」ずは䜕ですか

暙準による副䜜甚は、揮発性オブゞェクトぞのアクセス、オブゞェクトの倉曎、I / Oラむブラリからの関数の呌び出し、たたはこれらのアクションの䞀郚を含む関数の呌び出しの結果です。 副䜜甚は、ランタむムの状態の倉化です。

䜕らかの匏を蚈算するず、䜕らかの出力が埗られたす。 結果に加えお、匏を評䟡するずランタむムが倉化する堎合、匏には副䜜甚があるず蚀われたす。

䟋

int x = y++; // «y»  int 


倉数「x」の初期化に加えお、倉数「y」の倀は、++挔算子の副䜜甚のために倉曎されたした。
たあ、それは明らかです。 シヌケンスのポむントにさらに。 「通過点」ずいう甚語の別の定矩は、スティヌブサミット「質問ず回答の蚀語C」、ブログ「comp.lang.c」の著者によっお䞎えられたした。
フォロヌアップポむントは、「ダストが萜ち着いた」時点であり、遭遇したすべおの副䜜甚が完了し、取り残されるこずが保蚌されおいたす。

C ++暙準で説明されおいる次のポむントは䜕ですか

暙準では、次のシヌケンスポむントが説明されおいたす。



「䞍定の動䜜」ずは䜕ですか

この芏栌では、§1.3.12で「䞍定の動䜜」ずいうフレヌズを定矩しおいたす。
未定矩の動䜜 -誀った゜フトりェア構成たたは誀ったデヌタの䜿甚の結果ずしお発生する可胜性のある動䜜であり、囜際暙準では芁件を課しおいたせん。 未定矩の動䜜は、芏栌に明瀺的に蚘茉されおいない状況でも発生する堎合がありたす。

蚀い換えれば、䞍定の行動ずは、錻から萜ちた錻くそから圌女の劊嚠で終わる、起こりうるあらゆるこずを意味したす。

䞍定の動䜜ずシヌケンスポむントの関係は䜕ですか

この質問に察する答えを知る前に、未定矩の動䜜、未指定の動䜜、実装定矩の動䜜の違いを理解する必芁がありたす。
䞍特定の動䜜暙準に準拠-暙準が2぀以䞊の可胜なオプションを提䟛し、特定の状況で遞択すべき明確な芁件を課さない動䜜。

䞍特定の動䜜は、次のような郚分匏の蚈算の結果です。

実装定矩の動䜜暙準に準拠は、正しいデヌタを持぀敎圢匏の゜フトりェア構成の動䜜であり、実装に䟝存したす実装ごずに文曞化する必芁がありたす。

この動䜜の䟋は、ポむンタヌのサむズです。 暙準に埓っお、ポむンタヌのサむズはコンパむラヌの特定の実装に䟝存したす。 特定の実装では、異なるタむプのポむンタヌのサむズも異なる堎合がありたす。
たた、特定の挔算子のオペランド、特定の匏の郚分匏、および副䜜甚が発生する順序を蚈算する手順が指定されおいないこずにも泚意しおください。

䟋

 int x = 5, y = 6; int z = x++ + y++; //  ,    x++  y++ 


別の䟋

 int Hello() { return printf("Hello"); } int World() { return printf("World !"); } int main() { int a = Hello() + World(); /**  «Hello World!»  «World! Hello» ^ |        **/ return 0; } 


§5/ 4では、芏栌は次のように述べおいたす
連続する2぀のポむント間で、スカラヌオブゞェクトは、匏を1回しか蚈算しないずきに、栌玍されおいる倀を倉曎する必芁がありたす。

これはどういう意味ですか

簡単に蚀えば、2぀の連続点の間の倉数を耇数回倉曎するこずはできたせん。 匏では、通垞、次のシヌケンスポむントは閉じおいるセミコロンにあり、前のシヌケンスポむントは前のステヌトメントの最埌にありたす。 匏には、䞭間シヌケンスポむントを含めるこずもできたす。
䞊蚘に基づいお、次の匏は未定矩の動䜜を䜜成したす。

 i++ * ++i; // i = ++i; // ++i = 2; // i   1  i = ++i +1 // i = (i,++i,++i); //      `++i`   `i` (`i`   1   2  ) 

しかし同時に

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


さらに暙準に埓っお-匏の叀い倀蚈算前は、保存された倀を決定するためにのみ䜿甚可胜です。
これは、倀ぞのアクセスがその倉曎に先行する可胜性のある匏が間違っおいるこずを意味したす。

䟋

 std::printf("%d %d", i,++i); // ,    –  (++i)    . 


別の䟋

 a[i] = i++ ; //  a[++i] = i ,  a[i++] = ++i  .. 


C ++ 0xにはフォロヌポむントがないず聞きたしたが、本圓ですか


はい、そうです。
「フォロヌアップポむント」の抂念は、フォロヌアップ関係[BEFORE AFTER]の掗緎された抂念でISO C ++委員䌚に眮き換えられたした。

フォロヌアップ関係ずは[BEFORE]
Sequenced Beforeは、次の関係です。
  • 非察称に
  • 掚移的に
  • 蚈算のペアずそれらから圢成される郚分的に順序付けられたセットの間に生じる


正匏には、これは、䞎えられた2぀の匏AずBに぀いお、Aが[DOに続く] Bの堎合、実行Aは実行Bに先行する必芁があるこずを意味したす。 順䞍同の蚈算は重耇する堎合がありたす。
AおよびBの蚈算は、A [DOに続く] BたたはB [DOに続く] Aのいずれかである堎合に䞍定に順序付けられたすが、正確に指定されおいない堎合。 䞍確かな順序の蚈算は亀差できたせんが、いずれかを最初に実行できたす。

C ++ 0xのコンテキストで「蚈算」ずいう蚀葉は䜕を意味したすか

C ++ 0xでは、匏たたは郚分匏の評䟡には䞀般に以䞋が含たれたす。


暙準は私たちに䌝えおいたす§1.9/ 14
完党な匏に関連付けられた各倀のカりントず副䜜甚[次の前に]蚈算される次の完党な匏に関連付けられた倀のカりントず副䜜甚。

簡単な䟋

 int x; x = 10; ++x; 


この䟋では、匏++ xに関連付けられた倀ず副䜜甚の蚈算、倀ず副䜜甚の蚈算[x = 10]の埌[埌に]。

結局のずころ、䞊蚘のこずず䞍定の振る舞いの間には䜕らかの぀ながりがあるはずですよね


もちろん、接続がありたす。

§1.9/ 15では、次のこずが蚀及されおいたす。
前述の堎合を陀き、特定の挔算子のオペランドたたは特定の匏の郚分匏の蚈算が乱れたす。

泚プログラム䞭に耇数回評䟡される完党な匏の順序付けられおいない、たたは曖昧に順序付けられた郚分匏は、必ずしも同じ順序で毎回蚈算されるずは限りたせん。

䟋

 int main() { int num = 19 ; num = (num << 3) + (num >> 3) ; } 


1「+」挔算子のオペランドの蚈算が乱れおいたす。
2挔算子「<<」および「>>」のオペランドの蚈算が乱れおいたす。

§1.9/ 15特定の挔算子のオペランドの倀の蚈算[前に続く]挔算子の結果の倀の蚈算。

これは、匏x + yで、倀「x」および「y」の蚈算が[x + yの蚈算の前に続く]こずを意味したす。

さお、もっず重芁なこずに

§1.9/ 15次のいずれかのむベントに関しお、スカラヌオブゞェクトの副䜜甚の発生が乱れおいる堎合
  • 同じオブゞェクトの別の副䜜甚の発生
  • このオブゞェクトの倀を䜿甚しお倀をカりントする

その堎合、プログラムの動䜜は䞍確実になりたす。


䟋
 f(i = -1, i = -1); 


この衚珟に぀いお説明したしょう。 䞀芋、関数の匕数の無秩序な蚈算はあいたいさを䌎いたせん。 ただし、そのような匏を最適化するコンパむラが、同じメモリでの操䜜䞭に倱敗する意芋ではアクションが䌌た䞀連の呜什を䜜成しないずいう正確な可胜性はありたせん。

「-1」を割り圓おる最良の方法は、倉数をれロにしおデクリメントするこずだずコンパむラが決定したず仮定したす。
呜什は次のように圢成できたすコマンドは条件付きです
 clear i decr i clear i decr i 


たたは、次のこずができたす。
 clear i clear i decr i decr i 


その埌、倀-2がiに保存されたす。

関数が呌び出されるず、倀の各蚈算ず、この関数の匕数の匏、たたは関数を呌び出す匏に関連付けられた副䜜甚[呌び出される前に]呌び出された関数の本䜓の匏たたは挔算子の実行。
異なる匕数に関連付けられた倀のカりントず副䜜甚が乱れおいたす。

プログラムの流れ


前に埩号化された甚語を䜿甚しお、プログラム実行フロヌをグラフィカルに衚すこずができたす。 次の図では、匏たたは郚分匏の蚈算をExずしお瀺し、シヌケンスポむントは、オブゞェクト "e"の副䜜甚 "k"はSk、eです。 蚈算のために名前付きオブゞェクトから倀を読み取る必芁がある堎合「x」を名前にしたす、Vxで蚈算を瀺したす。残りの堎合-先ほど合意したEx。 匏の巊右に副䜜甚を曞き留めたす。 2぀の匏の境界は、䞊の匏が䞋の匏に評䟡されるこずを意味したす倚くの堎合、䞋の匏は䞊の匏のprvalueたたは巊蟺倀に䟝存するため。
2぀のi ++匏の堎合。 i ++; 図は次のようになりたす。

 E(i++) -> { S(increment, i) } | % | E(i++) -> { S(increment, i) } | % 


ご芧のずおり、この堎合、次の2぀のポむントがありたす。1぀は「i」の2぀の倉曎を分離したす。
関数呌び出しも興味深いものですが、それらの図は省略しおいたす。

 int c = 0; int d = 0; void f(int a, int b) { assert((a == c - 1) && (b == d - 1)); } int main() { f(c++, d++); } 


関数fの本䜓が実行を開始するたでに、匕数の蚈算によっお生成されるすべおの副䜜甚が終了するこずが保蚌されおいるため、このコヌドは正しいです。「c」ず「d」は1増加したす。
ここで、匏i ++ * j ++を考えたす。
 { S(increment, i) } <- E(i++) E(j++) -> { S(increment, j) } \ / +--+--+ | E(i++ * j++) | % 


2぀のブランチはどこから来たのですか シヌケンスポむントは、発生する前に実行された蚈算を完了するこずを思い出しおください。 乗算のすべおの郚分匏は、乗算自䜓の前に蚈算されるため、この匏にはシヌケンスポむントがなくなりたす。したがっお、オペランドの蚈算の理論的な「䞊列性」を考慮しお、同じオブゞェクトの競合倉化が発生する堎所を瀺唆する必芁がありたす。 より正匏には、これら2぀のブランチは無秩序です。 フォロヌアップポむントは、いく぀かの蚈算を順序付けし、他の蚈算を順序付けしない関係です。 T.O. 前述のように、シヌケンスポむントは半順序です。

矛盟する副䜜甚。

コンパむラにマシンコヌドを生成および最適化する自由を提䟛するために、䞊蚘で怜蚎した乗算ず同様の堎合、郚分匏を蚈算する手順は確立されず、それらによっお生成される副䜜甚は分離されたせん䞊蚘の堎合を陀く。
これにより競合が発生する可胜性があるため、暙準は、シヌケンスポむントを䜿甚せずに同じオブゞェクトを倉曎しようずするず、プログラムの動䜜を無期限に呌び出したす。 これはスカラヌオブゞェクトに適甚されたす。残りのオブゞェクトは䞍倉配列であるか、単にこの芏則クラスオブゞェクトに該圓しないためです。 未定矩の動䜜は、匏にオブゞェクトの以前の倀ぞの呌び出しずその倉曎i * i ++などが含たれる堎合にも発生したす
 //    ! //  ,    'i'   «» : V(i) E(i++) -> { S(increment, i) }) \ / +---+---+ | E(i * i++) | % 

䟋倖ずしお、新しい倀を蚈算する必芁がある堎合は、オブゞェクトの倀を読み取るこずができたす。 コンテキストの䟋i = i + 1
  V(i) E(1) \ / +---+---+ | E(i) E(i + 1) \ / +-------+-------+ | E(i = i + 1) -> { S(assign, i) } | % 


ここで、右偎に「i」ぞのアピヌルがありたす。 䞡方の郚分を蚈算した埌、割り圓おが行われたす。 T.O. 副䜜甚ず「i」の呌び出しはシヌケンスポむントを超えるこずなく発生したすが、栌玍された倀を決定するためだけに「i」に切り替えたため、意芋の盞違はありたせん。
時々、倀は倉曎埌に読み取られたす。 ケヌス甚
a =b = 0;
「b」にレコヌドがあり、シヌケンスポむントを超えるこずなく「b」から読み取るこずは事実です。 それにもかかわらず、新しい倀「b」はすでに読み取られおおり、叀い倀にはアクセスされないため、これは正垞です。 この堎合、割り圓お「b」の副䜜甚は、シヌケンスの次のポむントたでだけでなく、割り圓お「a」に必芁な倀「b」を読み取る前に期限切れになりたす。 暙準では、「割り圓お操䜜の結果は、割り圓おが完了した埌、巊オペランドに栌玍された倀です結果は巊蟺倀」ず明瀺されおいたす。 シヌケンスポむントの抂念を䜿甚しないのはなぜですか この抂念には、読み取りが行われる巊蟺倀を返す割り圓おの副䜜甚のみを考慮するのではなく、この状況で巊右のオペランドのすべおの副䜜甚を完了する必芁がないずいう芁件が含たれおいるためです。

゜ヌス

Source: https://habr.com/ru/post/J216189/


All Articles