自己加算演算子の葛藤について

10350 ワード

問題が発生しました.
1     int a=5;

2     printf("%d
",a++ + ++a);

出力は12、その後aは7である.
木有悬念最初のa後++は5である.2番目のa前++は6である.式の値を計算し、このときオペランドの値はaから取るので、6+6=12、出力します.最後に最初のaを完成する後++は、aは7である.
それから...思ったよ
1     int a=5;

2     printf("%d
",a++ + ++a + a++); //(1) 3 a=5; 4 printf("%d
",a++ + ++a + ++a); //(2)

神馬になるの?
私の慣性理解:(1)は18無懸念、(2)は?21でしょう...先に完成した後の2つのaの前++で、a値は7です.式の値7+7+7=21を計算します.最後にもう一つ後++をして、aは8です.
しかしgccのコンパイル結果:19! 
不服な私は紙の上で長い間漕いでいた...ゞ乃个纠结啊..ゞそしてVS 2005に行って一周しましたが、あれ?VSコンパイル結果は21.
私はまず平然としているような気がします(小様!兄も間違いなく、コンパイラが災いを招いたのです^^).
しかし、私はまた葛藤し続け、先生にメールを送った.閔先生は責任を持って答えました.
a++++a++a++aを例にとると、gccにおける処理ステップは、S 1:a++式の値を計算し、後++であるため、aの値は変わらず、依然として5である.S 2:左数の1番目の++a式の値を計算し、それが前++であるため、aの値は6になる.S 3:上記の2つの式の和を計算すると、増減式の値は操作数aから直接取得され、一時変数は使用されないが、このときaの値はすでに6,6+6=12であるため、a+++++a式の値は12であり、この12は中間結果として一時変数またはCPUレジスタに保存される.S 4:左数の2番目の++a式の値を計算し、それが前++であるため、aの値は7となる.S 5:一時変数またはCPUレジスタに格納された中間結果をS 4で得られた結果に加算し、12+7=19、式全体の値を19とする.S 6:式に後++が1つあるので、aをもう一度加える必要があり、aの値は8になります.
他のコンパイラの処理は少し異なり、3つのサブエクスプレッション(a++、++a、++a、++a)を先に計算する可能性があります.この場合、aの値は7で、この3つのサブエクスプレッションの計算結果を合計します.同様に、自己増減エクスプレッションは一時変数ではなくオペランドaから直接値を取るため、実際に計算に参加するのは7+7+7=21です.肝心な違いは、gccはまずa+++++aを計算し、この中間結果を最後の++aに加算することである.vcは、a++、++a、++aをすべて計算した後、式全体の値を計算します.
そのため、gccとVSコンパイルで生成されたアセンブリコードを比較しました.
gcc:
 1     movl    $5, -4(%ebp)

 2     leal    -4(%ebp), %eax

 3     incl    (%eax)       ;++a,a 6 

 4     movl    -4(%ebp), %eax

 5     movl    %eax, %edx

 6     addl    -4(%ebp), %edx  ;a++ + ++a    12   edx

 7     leal    -4(%ebp), %eax

 8     incl    (%eax)       ;++a,a 7 <=====  9     movl    %edx, %eax

10     addl    -4(%ebp), %eax  ;12+7=19

11     movl    %eax, 4(%esp)

12     leal    -4(%ebp), %eax

13     incl    (%eax)

14     movl    $LC0, (%esp)

15     call    _printf

 
VS2005:
 1     a=5;

 2 00411450  mov         dword ptr [a],5 

 3     printf("%d
",a++ + ++a + ++a); 4 00411457 mov eax,dword ptr [a] 5 0041145A add eax,1           ;++a,a 6 6 0041145D mov dword ptr [a],eax 7 00411460 mov ecx,dword ptr [a] 8 00411463 add ecx,1           ;++a,a 7 <===== 9 00411466 mov dword ptr [a],ecx 10 00411469 mov edx,dword ptr [a]    ;edx=7  11 0041146C add edx,dword ptr [a]    ;edx+a edx 14 12 0041146F add edx,dword ptr [a]    ;edx+a edx 21  13 00411472 mov dword ptr [ebp-0D0h],edx 14 00411478 mov eax,dword ptr [a] 15 0041147B add eax,1 16 0041147E mov dword ptr [a],eax 17 00411481 mov esi,esp 18 00411483 mov ecx,dword ptr [ebp-0D0h] 19 00411489 push ecx 20 0041148A push offset string "%d
" (41573Ch) 21 0041148F call dword ptr [__imp__printf (4182BCh)] 22 00411495 add esp,8 23 00411498 cmp esi,esp 24 0041149A call @ILT+320(__RTC_CheckEsp) (411145h)

 
同じように、似たような状況は次のとおりです.
1     int a=5;

2     printf("%d
",++a + ++a + ++a); //(1) 3 a=5; 4 printf("%d
",++a + (++a + ++a)); //(2)

(1)gcc出力22,VS 2005出力24
(2)gccもVS 2005も24出力!
(2)について、括弧の添加は3つのサブ式の結合順序を変更し、gccでも前の2つの++aサブ式の和を事前に計算することはできず、最後の++a計算が完了してから式全体の和を求める必要があり、このときaの値は8に加算される.これはvc固有の計算過程とちょうど一致する.
 
まとめ:このような問題は私たちの天朝中国風の試験問題の中にしか現れない.それは難解な意味を体現しているが、プログラミングの実践の中で、同じ変数に対する複数の自増減演算を、同じ表現式に置くことは厳しく禁止されている.自分に教えて、このようなスタイルのコードを書かないでください!表葛藤!