int i=0; i = i++ + ++i;
というものを見つけまして、すぐにiに入る値が想像できなかったので、
こういうときは実際に実行してみれば判ると思い、普通にコードを書いた。
ちなみに、0+2が行われるので2が入ると予想してました。
実験
#include <stdio.h> int main(){ int i=0; i=i++ + ++i; printf("%d\n", i); }
3
あ・・・あれ・・・?
演算は左から行われるのだから、0+2になるんじゃないのか。
デバッガで中身を見るとこんな感じになってた
004013C0 /$ 55 PUSH EBP 004013C1 |. 89E5 MOV EBP,ESP 004013C3 |. 83E4 F0 AND ESP,FFFFFFF0 004013C6 |. 83EC 20 SUB ESP,20 004013C9 |. E8 52060000 CALL c.00401A20 004013CE |. C74424 1C 0000>MOV DWORD PTR SS:[ESP+1C],0 ; | i=0; 004013D6 |. FF4424 1C INC DWORD PTR SS:[ESP+1C] ; | ++i; 004013DA |. 8B4424 1C MOV EAX,DWORD PTR SS:[ESP+1C] ; | eax = i; 004013DE |. 01C0 ADD EAX,EAX ; | eax += eax; 004013E0 |. 894424 1C MOV DWORD PTR SS:[ESP+1C],EAX ; | i = eax; 004013E4 |. FF4424 1C INC DWORD PTR SS:[ESP+1C] ; | ++i; 004013E8 |. 8B4424 1C MOV EAX,DWORD PTR SS:[ESP+1C] ; | 004013EC |. 894424 04 MOV DWORD PTR SS:[ESP+4],EAX ; | 004013F0 |. C70424 6430400>MOV DWORD PTR SS:[ESP],c.00403064 ; |ASCII "%d" 004013F7 |. E8 94080000 CALL; \printf 004013FC |. C9 LEAVE 004013FD \. C3 RETN
i=0のとき、i++ + ++iを分解すると、
i=i++;の結果はi=1となり、i+=++iは、i+=(1+i)となって、i+=i;i+=1;としていると予想。
i=i++;の演算を行った後、
その計算結果をレジスタeaxにいれて、
i + i = eax + eaxを行い、
その結果をiに代入した後に、
++iの評価が行われることで、iが3になった感じですかね。
最後にeaxにいれてるのは、printfの引数として渡すために、一度レジスタにコピーしているだけです。
スタックからスタックへのコピーはできないので、一度レジスタを経由しています。
ちなみにこの式は最適化オプションをつけると、定数展開されます。コンパイラ君は賢いですねえ。
でも少し疑問がでてきた。
i=++i + i++を行った場合どうなるのか?
一度変数に代入せず、この演算の結果を直接引数にしてみたらどうなるのか?
この組み合わせは4パターンあるので、全部やってみた。
#include <stdio.h> int main(){ int i=0; i=i++ + ++i; printf("%d\n", i); i=0; i = ++i + i++; printf("%d\n", i); i=0; printf("%d\n", i++ + ++i); i=0; printf("%d\n", ++i + i++); }
3
3
2
2
i++ + ++iと++i + i++というように、左辺と右辺を入れ替えても、結果は同じということらしい。
デバッガで見てみると、どちらも同じコードを生成していました。
で、引数として、演算結果を直接渡した場合、2になるらしい。
もう一個変数を作って、演算結果をiではなくそっちにいれるようにすれば、同じく2になった。
ほほー。
ならPerlでも同じなのかな!
$i=0; $i = $i++ + ++$i; printf("%d\n", $i); $i=0; $i = ++$i + $i++; printf("%d\n", $i); $i=0; printf("%d\n", $i++ + ++$i); $i=0; printf("%d\n", ++$i + $i++);
2
3
2
3
違うやん・・・
まとめ
結果がわからない演算はするもんじゃない。