9

私は次のコードを持っています

int m[4]={1,2,3,4}, *y; 
y=m; 
*y = f(y++); // Expression A

私の友人は、それExpression Aは明確に定義された行動をしていると私に言いましたが、彼が正しいかどうかはわかりません。

彼によると、機能はその間にf()導入するsequence pointため、動作は明確に定義されています。

誰かが明確にしてください。

PS:実用的な目的でそのようなコードを書くべきではないことを私は知っています。それはただ学ぶことを目的としています。:)

4

4 に答える 4

15

せいぜい、問題のコードの動作は特定されていません。代入演算子の場合、「オペランドの評価の順序は指定されていません」(C99§6.5.16/ 4)。

左のオペランドを最初に評価すると、の結果f(y++)がに格納されm[0]ます。右のオペランドが最初に評価されると、結果はに格納されm[1]ます。

動作が未定義であるかどうかについては、関連する段落は次のとおりです。

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

左側が最初に評価される場合、順序は次のとおりであるため、2番目の文に反することになります。

  1. の値はy、間接参照するために左側で読み取られます
  2. の値はy、それをインクリメントするために右側で読み取られます
  3. 関数への引数の評価後にシーケンスポイントがあります(したがって、の副作用y++は完全であり、yに書き込まれます)

ステップ1では、の「前の値」yが読み取られますが、「保存する値の決定」以外の目的で行われます。したがって、1つの有効な評価順序が未定義の動作を生成するため、動作は実際には未定義です。

于 2010-07-24T16:23:48.550 に答える
13

シーケンスポイントを導入する関数呼び出しについては、絶対に正しいです。ただし、そのシーケンスポイントは、ケースの状況を保存しません。

最初にこの簡単な例を考えてみましょう

i = some_function(i++);

有効ですか?はい、そうです。なんで?関数によって導入されたシーケンスポイント(あなたが話しているもの)が2つの変更をi互いに分離し、コードを有効にするため、これは有効です。この式の評価の順序はありません。その結果、iシーケンスポイントが介在せずに2回変更されます。

ただし、バリアントに戻りましょう

*y = f(y++);

この場合、そのシーケンスポイントも存在します。ただし、言語は=演算子の評価の順序を保証しません(つまり、言語は代入演算子のどのオペランドが最初に評価されるか(左または右)を保証しません)。コンパイラが最初に左側を評価し()、次に*y関数の引数を評価し(y++)、次に関数を呼び出してから実際の割り当てを実行することは非常に許容されます。この潜在的なシナリオでは、最初の2つのステップ(読み取りyと変更)はyシーケンスポイントで区切られていません。したがって、動作は定義されていません。

于 2010-07-24T16:50:28.137 に答える
1

式は明確に定義されていません:

式の有効な解釈は次のとおりです。

(1) int* t0 = y++; 
(2) int  t1 = f(t0);
(3) int& t2 = *y;
-----------------
t2 = t1; 

式の同様に有効な解釈は次のとおりです。

(1) int& t2 = *y;
(2) int* t0 = y++; 
(3) int  t1 = f(t0);
-----------------
t2 = t1; 

これらは両方とも無駄であり、異なる結果を生成します。したがって、式の結果は未定義です。

于 2010-07-24T16:46:36.313 に答える
0

編集:これは正しくありませんが、コメントに続く議論がやや明るいので、ここに残しておきます。貴重なものになることを願っています。

これは、C(またはC ++)の演算子の評価順序に基づいて明確に定義されています。

割り当ては、最初に式の右側の評価を強制します。関数適用は最初に引数の評価を強制するので、効果はかなり明確に見えます(私はそれを実行しようとしたことがないので、遠慮なく修正してください!)。一時変数(私はそれらをt0とt1と呼びます)を使用してこれを書き直すことができます、そしてこれはもう少し明確かもしれないと思います:

t0 = y++;
t1 = f(t0);
*y = t1;

「シーケンスポイント」という用語は、少し赤いニシンです。シーケンスポイントは実際には作成されません。むしろ、言語に対して厳密な評価順序が定義されている結果です。

編集:この回答は知的に満足のいくように見えますが、James McNellisの回答は、割り当ての評価順序が明確に定義されていないことを示すC99仕様の関連部分を引用しています。彼の事実を実際にチェックしたことに対する彼の完全な功績。ほとんどのコンパイラがそのようなコードを発行する順序を定期的に変更する可能性は低いと思うので、答えを「明確に定義されている」から「特定のコンパイラに関しておそらく明確に定義されている」に修正します(非常に積極的な最適化を説明するために、「おそらく」と言います)。

于 2010-07-24T16:06:05.350 に答える