4

重複の可能性:
未定義の動作とシーケンスポイント

標準で定義されているように、E1 + = E2は、E1が1回だけ評価されることを除いて、E1 = E1+E2とほぼ同じです。したがって、さらに、 "p + =(* p)++ + c"; 未定義の動作を引き起こしますか?

gcc / g ++(4.7 / 4.4)で次のコードを試してください。結果には、bxxxxx(g ++ 4.7)またはaxbxxx(gcc、g ++ 4.4)の2種類があります。コードで(1)を実行しているが、(2)を実行していない場合、axbxxxしか取得できません。

#include <stdio.h>

int main() {
    char s[] = "axxxxx";
    char *p = s;

    printf("s = %s in the beginning.\n"
           "p is pointed at the %d-th char.\n", s, p - s);
    //p = p + (*p)++ * 3 + 2 - 'a' * 3; // (1)
    p += (*p)++ * 3 + 2 - 'a' * 3; // (2)
    printf("p is moved ahead by %d steps\n", p - s);
    printf("s = %s after the operation.\n", s);
    return 0;
}

なぜそれが未定義の振る舞いを引き起こすのか、またそれがgccのバグであると断言することもできません。

axbxxxの結果については、オペランドまたはポスト++が2回評価される理由も理解できません(値を取得して後で保存する)。規格では「1…追加」と書いてありますので、一度だけ評価してください。post ++のオペランドのアドレスが一度だけ評価される場合、割り当てが実行される順序に関係なく、式の効果は同じになります。

===更新===

最初のコメントにリンクされているドキュメントを読んだ後、次のルールが重要になると思います。

「2)さらに、前の値は、保存される値を決定するためにのみアクセスされるものとします。」。

したがって、「p = p +(* p)++ * 3 + c」でのpのアクセスは、*pに格納される値とは関係のない*pの「事前値」の一部と見なされますか。 ?

IMO、このルールに違反していません。

4

4 に答える 4

3

いいえ、がを指していないp = p + (*p)++ * 3 + cと仮定すると、未定義の動作は発生しません。pc

この場合、疑わしい部分は、*p式内の値の読み取りと変更です。ただし、その値はの新しい値を決定する目的で読み取られるため(の新しい値は、読み込まれた値pに直接データ依存します)、要件に違反しません。p*p

コンパイラのバグは、実際には不特定の状況での誤った動作に起因していると思います。式には2つの副作用があることに注意してください。p新しい値をに格納することと、新しい値をに格納することです*p。これらの副作用が発生する順序は特定されていません。ただし、(*p)++部分式の評価中に、コンパイラはの特定の左辺値引数を「修正」++して、新しい(インクリメントされた)値がその正確なオブジェクトに格納されていることを確認することになっています。古いバージョンのコンパイラはそれを実行できなかったようです。つまり、の新しい値がp最初に評価され、次にの新しい値がの新しい値を介し*p格納されます。これは明らかに正しくありません。p

于 2012-10-10T16:10:14.393 に答える
1

原則として、ステートメントp += (*p)++ + c;は潜在的に正しいです。それが行うのは、ポインタ(p)をある値だけ進めることです。これは、ポイントする変数によって決定されますp

pを超えてインクリメントしないようにする必要がありますs + 7。コードを注意深くチェックして、それが当てはまるかどうかを確認しませんでした(ただし、特定のエンコーディング隣接性の仮定を行っていることに注意してください)。

于 2012-10-10T16:19:18.163 に答える
1

p += x;と同等ではなくp = p + x;、と同等であることに注意してくださいp = p + (x);x最初に評価され、結果がpに追加されます。タイトルに示されているように括弧のない式では、の中間値pが配列の外側を指している可能性がありますが、これは実際には未定義の動作です。xの結果が配列内にある限り、コード内のバージョンは問題ありません。

6.5.16.2.3形式E1op= E2の複合代入は、左辺値E1が1回だけ評価されるという点でのみ、単純代入式E1 = E1 op(E2)とは異なります。

J.2未定義動作-配列オブジェクトおよび整数型への、またはそのすぐ先のポインターの加算または減算は、同じ配列オブジェクト(6.5.6)を指さない、またはそのすぐ先の結果を生成します。

このUB定義は、割り当ての最終結果に限定されません。

于 2012-10-11T05:24:59.000 に答える
0

p += (*p)++ * 3 + 2 - 'a' * 3の形式ではありませんE1 = E1 + E2

  • 右側にはポインタ(アドレス変数)があります
  • 左側で、このポインターによってアドレス指定された変数をインクリメントします。

編集:発見p+=

の右側の各式の評価の順序が何であれ、E1 = E1 + E2の値pは変更されないため、まだ未定義ではありません。

于 2012-10-10T16:14:31.507 に答える