2015年7月26日 星期日

c/c++ 語言的 前++ 與 後++

int i=5;
i = i++;
這個惱人的問題這篇有解釋, 是未定義行為。有些面試題目會考這個, 實在是折磨人。

我想知道前++ 與後++ 到底有什麼差別, 組合語言之前沒有祕密, 好久不見的組語再次出動了, 這次的很簡單, 我還補上圖示, 沒問題的。

fig1

後++ L22 有個 lea 指令, 這個有點難懂, 請參考以下連結。
所以在 x=i++ 後, x=5, i=6。

lea:
比较汇编指令 LEA 和 MOV
有意思的lea指令

fig1 是我分解反組譯後的圖示, 1 ~ 4 的步驟分別說明這個後++ 的行為。

後++
 1 #include <stdio.h>
 2 
 3 int main(int argc, char *argv[])
 4 {
 5   int x=1, i=5;
 6   x = i++;
 7   printf("x: %d, i: %d\n", x, i);
 8   return 0;
 9 }
10 
11 080483fb <main>:
12  80483fb:       8d 4c 24 04             lea    0x4(%esp),%ecx
13  80483ff:       83 e4 f0                and    $0xfffffff0,%esp
14  8048402:       ff 71 fc                pushl  -0x4(%ecx)
15  8048405:       55                      push   %ebp
16  8048406:       89 e5                   mov    %esp,%ebp
17  8048408:       51                      push   %ecx
18  8048409:       83 ec 14                sub    $0x14,%esp
19  804840c:       c7 45 f4 01 00 00 00    movl   $0x1,-0xc(%ebp)
20  8048413:       c7 45 f0 05 00 00 00    movl   $0x5,-0x10(%ebp)
21  804841a:       8b 45 f0                mov    -0x10(%ebp),%eax
22  804841d:       8d 50 01                lea    0x1(%eax),%edx
23  8048420:       89 55 f0                mov    %edx,-0x10(%ebp)
24  8048423:       89 45 f4                mov    %eax,-0xc(%ebp)
25  8048426:       83 ec 04                sub    $0x4,%esp
26  8048429:       ff 75 f0                pushl  -0x10(%ebp)
27  804842c:       ff 75 f4                pushl  -0xc(%ebp)
28  804842f:       68 e0 84 04 08          push   $0x80484e0
29  8048434:       e8 97 fe ff ff          call   80482d0 <printf@plt>
30  8048439:       83 c4 10                add    $0x10,%esp
31  804843c:       b8 00 00 00 00          mov    $0x0,%eax
32  8048441:       8b 4d fc                mov    -0x4(%ebp),%ecx
33  8048444:       c9                      leave
34  8048445:       8d 61 fc                lea    -0x4(%ecx),%esp
35  8048448:       c3                      ret  
36 
37 

fig2

那如果是 i = i++ 會怎樣, 依樣畫葫蘆, 把 x (-0xc) 用 i 代替, 而改到 -0xc, 就等於改動 -0x10, 因為現在把這兩個位址看成是同一個, 這就是 fig2 的動作, 以這個反組譯來說, i 最後會得到 5。在步驟 2 的時候, i = 6, 但是到了步驟 4, i 變成了 5。所以若是步驟 2, 4 顛倒的話, 最後結果就是 i=6。



 前++ 就單純多了, 但我不知道這是不是標準行為。




前++
38 #include <stdio.h> 
39  
40 int main(int argc, char *argv[]) 
41 { 
42   int x=1, i=5; 
43   x = ++i; 
44   printf("x: %d, i: %d\n", x, i); 
45   return 0;  
4647   
48  80483fb:       8d 4c 24 04             lea    0x4(%esp),%ecx  
49  80483ff:       83 e4 f0                and    $0xfffffff0,%esp  
50  8048402:       ff 71 fc                pushl  -0x4(%ecx)  
51  8048405:       55                      push   %ebp  
52  8048406:       89 e5                   mov    %esp,%ebp  
53  8048408:       51                      push   %ecx  
54  8048409:       83 ec 14                sub    $0x14,%esp  
55  804840c:       c7 45 f4 01 00 00 00    movl   $0x1,-0xc(%ebp)  
56  8048413:       c7 45 f0 05 00 00 00    movl   $0x5,-0x10(%ebp)  
57  804841a:       83 45 f0 01             addl   $0x1,-0x10(%ebp)  
58  804841e:       8b 45 f0                mov    -0x10(%ebp),%eax  
59  8048421:       89 45 f4                mov    %eax,-0xc(%ebp)  
60  8048424:       83 ec 04                sub    $0x4,%esp  
61  8048427:       ff 75 f0                pushl  -0x10(%ebp)  
62  804842a:       ff 75 f4                pushl  -0xc(%ebp)  
63  804842d:       68 e0 84 04 08          push   $0x80484e0  
64  8048432:       e8 99 fe ff ff          call   80482d0 <printf@plt>  
65  8048437:       83 c4 10                add    $0x10,%esp  
66  804843a:       b8 00 00 00 00          mov    $0x0,%eax  
67  804843f:       8b 4d fc                mov    -0x4(%ebp),%ecx  
68  8048442:       c9                      leave  
69  8048443:       8d 61 fc                lea    -0x4(%ecx),%esp  
70  8048446:       c3                      ret     





所以後++ 需要多一個東西來暫存中間結果, 這就是為什麼 c++ 建議用前++, 而少用後++, 這樣就不需要多一個東西來暫存, 速度自然也快一些。

不過編出來的執行檔大小都一樣, 我不知道速度是不是前++ 會快些, 不知道還有什麼魔法。

2 則留言:

  1. 中正 研究所的 考題 似乎曾經考過類似的 印象中...

    回覆刪除
  2. 看到這種題目應該會罵髒話吧?

    回覆刪除

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。