あなたの質問はおそらく、「なぜこれらの構造はCで未定義の振る舞いなのか?」ではありませんでした。あなたの質問は、おそらく「なぜこのコード(を使用して++
)が私に期待した値を与えなかったのですか?」
この答えはその質問に答えようとします:コードがなぜあなたが期待した答えを与えなかったのか、そして期待通りに機能しない式を認識(そして回避)する方法をどのように学ぶことができますか?
C ++
と--
演算子の基本的な定義、および接頭辞の形式++x
と接尾辞の形式の違いについてはすでにお聞きになったと思いますx++
。しかし、これらの演算子は考えるのが難しいので、あなたが理解していることを確認するために、おそらくあなたは次のようなものを含む小さな小さなテストプログラムを書いたでしょう
int x = 5;
printf("%d %d %d\n", x, ++x, x++);
しかし、驚いたことに、このプログラムは理解を助けませんでした -奇妙で予想外の不可解な出力を出力しました。++
しました。
または、次のような理解しにくい表現を見ているかもしれません。
int x = 5;
x = x++ + ++x;
printf("%d\n", x);
たぶん誰かがそのコードをパズルとしてあなたに与えました。このコードは、特に実行した場合も意味がありません。2つの異なるコンパイラーでコンパイルして実行すると、2つの異なる答えが得られる可能性があります。どうしたの?どちらが正しいですか。(そして答えは、両方ともそうであるか、どちらもそうではないということです。)
これまでに聞いたように、これらの式はすべて未定義です。つまり、C言語はそれらが何をするかを保証しません。これは奇妙で驚くべき結果です。なぜなら、コンパイルして実行する限り、作成できるプログラムはどれも、一意で明確な出力を生成すると考えていたからです。しかし、未定義の動作の場合、それはそうではありません。
式が未定義になるのはなぜですか?++
およびを含む表現--
常に未定義ですか?もちろん、これらは便利な演算子であり、適切に使用すれば、完全に明確に定義されます。
式について、それらが未定義になる理由について話しているのは、一度に多くのことが行われている場合、どの順序で発生するかわからない場合、ただし順序が重要な結果である場合です。
この回答で使用した2つの例に戻りましょう。私が書いたとき
printf("%d %d %d\n", x, ++x, x++);
問題は、を呼び出す前にprintf
、コンパイラがx
最初に、またはx++
、または多分++x
?しかし、それはわかりません。Cには、関数への引数が左から右、右から左、またはその他の順序で評価されるという規則はありません。私たちは、コンパイラがどうなるかを言うことができないので、x
まず、それから++x
、それからx++
、またはx++
、その後++x
、その後x
、または他のいくつかのため。ただし、コンパイラーが使用する順序によっては、によって出力される結果が異なるため、順序が重要であることは明らかですprintf
。
このクレイジーな表現はどうですか?
x = x++ + ++x;
この表現の問題点は、xの値を変更するには、3つの異なる試みが含まれていることである:(1)x++
の部分は、Xに1を追加して、新しい値を保存しようとするx
、との古い値を返しますx
。(2)++x
パーツはxに1を追加し、新しい値をに格納して、の新しい値x
を返しx
ます。(3)x =
パーツは他の2つの合計をxに割り当てようとします。これらの3つの試みられた割り当てのどれが「勝つ」でしょうか?3つの値のどれが実際に割り当てられx
ますか?繰り返しになりますが、おそらく驚くべきことに、Cには言うべき規則はありません。
優先順位、結合性、または左から右への評価は、物事がどのような順序で行われるかを示していると想像するかもしれませんが、そうではありません。あなたは私を信じていないかもしれませんが、私の言葉を使ってください、そして私はそれをもう一度言います:優先順位と結合性はCの式の評価順序のすべての側面を決定するわけではありません。特に、1つの式内に複数の新しい値をx
、優先順位、結合性などに割り当てようとするさまざまな場所では、それらの試みのどちらが最初に発生したのか、最後に発生したのか、何でも発生しません。
ですから、これらすべての背景と導入を踏まえて、すべてのプログラムが明確に定義されていることを確認したい場合、どの式を記述でき、どの式を記述できないのでしょうか。
これらの式はすべて問題ありません。
y = x++;
z = x++ + y++;
x = x + 1;
x = a[i++];
x = a[i++] + b[j++];
x[i++] = a[j++] + b[k++];
x = *p++;
x = *p++ + *q++;
これらの式はすべて未定義です。
x = x++;
x = x++ + ++x;
y = x + x++;
a[i] = i++;
a[i++] = i;
printf("%d %d %d\n", x, ++x, x++);
そして最後の質問は、どの式が明確に定義されており、どの式が未定義であるかをどのようにして知ることができるかということです。
先に述べたように、未定義の式とは、一度に多くのことが行われ、どのような順序で問題が発生するか、順序が重要であるものです。
- 2つ以上の異なる場所で変更(割り当て)される変数が1つある場合、どの変更が最初に行われるかをどのようにして知ることができますか?
- ある場所で変更される変数があり、その値が別の場所で使用されている場合、古い値と新しい値のどちらを使用しているかをどのようにして知ることができますか?
#1の例として、式
x = x++ + ++x;
`x。を変更する3つの試みがあります。
#2の例として、式
y = x + x++;
の値を使用し、x
それを変更します。
それが答えです。作成する式では、各変数が最大1回変更されることを確認してください。変数が変更された場合、その変数の値を他の場所で使用しないでください。