5-5. インクリメント・デクリメント
さて、変数を増減させるには代入式を使えば良いことがわかりました。
しかし、カウンターなど単に1つ増やしたい時のためにもっと簡単な表記法があります。
それはインクリメントです。記号は ++ で単項演算子になります。
しかも、前置型と後置型とあり、また一つ減少させるデクリメントも存在します。
それらをまとめると、
++a , a++ aを1増加 a = a + 1 または a += 1 と等価
–a . a– aを1減少 a = a + 1 または a += 1 と等価
があります。
それでは、何故に前置と後置があるのか?
それは式としての値の振る舞いが違うために2種類存在します。
等価式の a = a + 1 , a += 1 は式全体の値として計算後aに代入する値と同じになります。
ここでハッキリ代入操作と計算が別次元である事を理解してください。この二つを混同すると次に言うことの意味がわからなくなります。
ということで計算した値を式の値とするのは前置のほうです。
もう一方はどんな値を取るのかというと計算する前の変数の値を式の値として扱います。
たとえば、a = 3 のとき
b = ++a;
a = 3; /* もう一度代入 */
c = a++;
としたときどちらともaは4になりますが、++aの値は4、a++の値は3になります。
よってbは4、cは3になります。
ものの本によっては代入してからインクリメントとかインクリメントしてから代入などと書かれていますが、最初からそうやって覚えると式の値と代入操作の区別がつきにくく初心者には混乱の元になります。 (実際、間違ってはないのですが)
<中級者向けプログラム>
これで、インクリメントとデクリメントをじっくりやってみましょう。
#include<stdio.h> main() { int a=1,b=1; printf("a = %d\n",a = a + a++ + a); printf("b = %d\n",b = b + ++b + b); }
たぶん今までの説明だけでは疑問だらけになると思います。
しかもこんなプログラムを試すような人もいないような気がするので、コンパイラによっては答えがちがうかも….
ちなみにVC++では
a = 3
b = 6
とでました。
[検証結果と補足説明]
まず、こちらで確認したコンパイラー
VC++、LSI-C
そのうち、
VC++は
a = 3
b = 6
LSI-Cは
a = 5
b = 5
となりました。
しかし、理論的には
a = 4
b = 5 または 6
でないといけないのです。(最終的にVC++はa=4になった)
ただ、前回2つ紹介したインクリメント・デクリメントの説明は両方とも正しいことが確認できました、しかしその両方の説明にはインクリメント・デクリメントのタイミングについては何も書いていませんでした。
つまり、代入のタイミングについては定義がなされていないことがこの2つのコンパイラーの仕様の違いとなって出てきたのだと思います。
それでは、どのように違ったのか?
a=1,b=1で
a = a + a++ + a
b = b + ++b + b
という計算をさせたとき、
VCでは、まずaの現在値1を読み取りa=1+1+1として代入(この時a=3)
この後に、aをインクリメント(だからprintfに埋め込んだ時3とでた)
次にb、これは計算の前にインクリメントしb=2+2+2として代入(b=6)
LSI-Cではa++の次のaはインクリメントされたa、++bの前のbはインクリメントされないことが確認、よって
a = 1 + 1 + 2
b = 1 + 2 + 2
となる、これで前回言っていた代入と計算が別次元であることの証明にもなったような…
ん、ではなぜLSI-Cでaは5になったのだ?
実は、これの解明に時間を費やしたのだが後置型の時、同じ変数が1つだけ左のオペランドにある場合、正常な値を出さないという不具合であることが確認されました。具体的に
a = a + a++ と
a = a + a + a++ の値が同じになるという変な現象が確認されました。