問題の C コード (「」の第 4 版で Bjarne Stroustrup が提供) C プログラミング言語」では、関数チェーンを使用して文字列を変更します。
<code class="cpp">void f2() { std::string s = "but I have heard it works even if you don't believe in it"; s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, ""); assert(s == "I have heard it works only if you believe in it"); }</code>
このコードは、文字列 s を変更する replace() 操作のチェーンを示しています。ただし、このコードは、GCC、Visual Studio、Clang などのさまざまなコンパイラ間で異なる動作を示すことが観察されています。
コードは単純に見えますが、不特定の順序が含まれます。特に関数呼び出しを伴う部分式の評価。 (すべての副作用は関数呼び出し内で発生するため) 未定義の動作は引き起こしませんが、未指定の動作は示します。
重要な問題は、s.find( などの部分式の評価順序です)。 "even") および s.find(" don't") は明示的に定義されていません。これらの部分式は、最初の s.replace(0, 4, "") 呼び出しの前後で評価でき、結果に影響を与える可能性があります。
コード スニペットの評価順序を調べると、次のようになります。
s.replace(0, 4, "").replace(s.find("even"), 4, "only").replace(s.find(" don't"), 6, "");
次の部分式が不定に順序付けされていることがわかります (括弧内の数字で示されています):
括弧の各ペア内の式は順序付けされています (例: 2 が 3 の前にある) が、相互に異なる順序で評価することができます。具体的には、式 1 と 2 の間、および式 1 と 4 の間に不確定性があります。
コンパイラの動作で観察された矛盾は、それぞれが選択した異なる評価順序に起因している可能性があります。コンパイラ。場合によっては、 replace() 呼び出しが期待どおりの動作をもたらす方法で評価されることもありますが、評価順序によって文字列が予期しない方法で変更される場合もあります。
例として、次のことを考えてみましょう。
次の点に注意することが重要です。このコードは、未定義の動作を呼び出すことはありません。未定義の動作には、通常、初期化されていない変数にアクセスしたり、境界外のメモリにアクセスしようとしたりすることが含まれます。この場合、すべての副作用は関数呼び出し内で発生し、コードは無効なメモリ位置にアクセスしません。
ただし、コードは指定されていない動作を示します。つまり、コードの正確な順序は部分式の評価は C 標準では定義されていません。これにより、コンパイラが異なると、同じプログラムを実行しても異なる結果が生じる可能性があります。
C 標準委員会はこの問題を認識し、式の評価順序を改良するための変更を提案しました。慣用的な C 。 C 20 の [expr.call]p5 に対する変更案では、「postfix-expression は、expression-list 内の各式およびデフォルト引数の前に順序付けされる」と指定されており、これにより、このコードでの指定されていない動作が排除されます。
以上が「C プログラミング言語」の関数チェーンは指定されていない動作を示しますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。